(function() {
    'use strict';

    if (!Array.prototype.fill) {
        Object.defineProperty(Array.prototype, 'fill', {
            value: function(value) {

                // Steps 1-2.
                if (this == null) {
                    throw new TypeError('this is null or not defined');
                }

                var O = Object(this);

                // Steps 3-5.
                var len = O.length >>> 0;

                // Steps 6-7.
                var start = arguments[1];
                var relativeStart = start >> 0;

                // Step 8.
                var k = relativeStart < 0 ?
                    Math.max(len + relativeStart, 0) :
                    Math.min(relativeStart, len);

                // Steps 9-10.
                var end = arguments[2];
                var relativeEnd = end === undefined ?
                    len : end >> 0;

                // Step 11.
                var final = relativeEnd < 0 ?
                    Math.max(len + relativeEnd, 0) :
                    Math.min(relativeEnd, len);

                // Step 12.
                while (k < final) {
                    O[k] = value;
                    k++;
                }

                // Step 13.
                return O;
            }
        });
    }

    if (!Array.prototype.find) {
        Object.defineProperty(Array.prototype, 'find', {
            value: function(predicate) {
                if (this == null) {
                    throw new TypeError('Array.prototype.find called on null or undefined');
                }
                if (typeof predicate !== 'function') {
                    throw new TypeError('predicate must be a function');
                }
                var list = Object(this);
                var length = list.length >>> 0;
                var thisArg = arguments[1];

                for (var i = 0; i !== length; i++) {
                    if (predicate.call(thisArg, this[i], i, list)) {
                        return this[i];
                    }
                }
                return undefined;
            }
        });
    }

    // Production steps of ECMA-262, Edition 6, 22.1.2.1
    if (!Array.from) {
        Array.from = (function() {
            var toStr = Object.prototype.toString;
            var isCallable = function(fn) {
                return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
            };
            var toInteger = function(value) {
                var number = Number(value);
                if (isNaN(number)) {
                    return 0;
                }
                if (number === 0 || !isFinite(number)) {
                    return number;
                }
                return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
            };
            var maxSafeInteger = Math.pow(2, 53) - 1;
            var toLength = function(value) {
                var len = toInteger(value);
                return Math.min(Math.max(len, 0), maxSafeInteger);
            };

            // The length property of the from method is 1.
            return function from(arrayLike /*, mapFn, thisArg */ ) {
                // 1. Let C be the this value.
                var C = this;

                // 2. Let items be ToObject(arrayLike).
                var items = Object(arrayLike);

                // 3. ReturnIfAbrupt(items).
                if (arrayLike == null) {
                    throw new TypeError("Array.from requires an array-like object - not null or undefined");
                }

                // 4. If mapfn is undefined, then let mapping be false.
                var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
                var T;
                if (typeof mapFn !== 'undefined') {
                    // 5. else
                    // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
                    if (!isCallable(mapFn)) {
                        throw new TypeError('Array.from: when provided, the second argument must be a function');
                    }

                    // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
                    if (arguments.length > 2) {
                        T = arguments[2];
                    }
                }

                // 10. Let lenValue be Get(items, "length").
                // 11. Let len be ToLength(lenValue).
                var len = toLength(items.length);

                // 13. If IsConstructor(C) is true, then
                // 13. a. Let A be the result of calling the [[Construct]] internal method
                // of C with an argument list containing the single item len.
                // 14. a. Else, Let A be ArrayCreate(len).
                var A = isCallable(C) ? Object(new C(len)) : new Array(len);

                // 16. Let k be 0.
                var k = 0;
                // 17. Repeat, while k < len… (also steps a - h)
                var kValue;
                while (k < len) {
                    kValue = items[k];
                    if (mapFn) {
                        A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
                    } else {
                        A[k] = kValue;
                    }
                    k += 1;
                }
                // 18. Let putStatus be Put(A, "length", len, true).
                A.length = len;
                // 20. Return A.
                return A;
            };
        }());
    }


    Array.prototype.includes = Array.prototype.includes || function(searchElement, fromIndex) {
        if (!this) {
            throw new TypeError('Array.prototype.includes called on null or undefined');
        }

        if (fromIndex === undefined) {
            var i = this.length;
            while (i--) {
                if (this[i] === searchElement) {
                    return true
                }
            }
        } else {
            var i = fromIndex,
                len = this.length;
            while (i++ !== len) { // Addittion on hardware will perform as fast as, if not faster than subtraction
                if (this[i] === searchElement) {
                    return true
                }
            }
        }
        return false;
    };

    // These polyfills are required for using Tick on IE 11
    if (typeof Object.assign != 'function') {
        Object.assign = function(target, varArgs) { // .length of function is 2
            if (target == null) { // TypeError if undefined or null
                throw new TypeError('Cannot convert undefined or null to object');
            }

            var to = Object(target);

            for (var index = 1; index < arguments.length; index++) {
                var nextSource = arguments[index];

                if (nextSource != null) { // Skip over if undefined or null
                    for (var nextKey in nextSource) {
                        // Avoid bugs when hasOwnProperty is shadowed
                        if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                            to[nextKey] = nextSource[nextKey];
                        }
                    }
                }
            }
            return to;
        };
    }


    // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
    if (!Object.keys) {
        Object.keys = (function() {
            var hasOwnProperty = Object.prototype.hasOwnProperty,
                hasDontEnumBug = !({
                    toString: null
                }).propertyIsEnumerable('toString'),
                dontEnums = [
                    'toString',
                    'toLocaleString',
                    'valueOf',
                    'hasOwnProperty',
                    'isPrototypeOf',
                    'propertyIsEnumerable',
                    'constructor'
                ],
                dontEnumsLength = dontEnums.length;

            return function(obj) {
                if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
                    throw new TypeError('Object.keys called on non-object');
                }

                var result = [],
                    prop, i;

                for (prop in obj) {
                    if (hasOwnProperty.call(obj, prop)) {
                        result.push(prop);
                    }
                }

                if (hasDontEnumBug) {
                    for (i = 0; i < dontEnumsLength; i++) {
                        if (hasOwnProperty.call(obj, dontEnums[i])) {
                            result.push(dontEnums[i]);
                        }
                    }
                }
                return result;
            };
        }());
    }

}());
/* eslint-disable */

/*
 * @pqina/flip v1.8.0 - A Beautifully Animated Flip Clock
 * Copyright (c) 2020 PQINA - https://pqina.nl/flip/
 */
(function(root, undefined) {
    'use strict';

    // only create tick extensions queue if not already available
    if (!root.Tick) {
        root.Tick = [];
    }

    // add this extension
    root.Tick.push(['view', 'flip', (function() {
        if (!module) {
            var module = {};
        }
        'use strict';

        var asyncGenerator = function() {
            function AwaitValue(value) {
                this.value = value;
            }

            function AsyncGenerator(gen) {
                var front, back;

                function send(key, arg) {
                    return new Promise(function(resolve, reject) {
                        var request = {
                            key: key,
                            arg: arg,
                            resolve: resolve,
                            reject: reject,
                            next: null
                        };

                        if (back) {
                            back = back.next = request;
                        } else {
                            front = back = request;
                            resume(key, arg);
                        }
                    });
                }

                function resume(key, arg) {
                    try {
                        var result = gen[key](arg);
                        var value = result.value;

                        if (value instanceof AwaitValue) {
                            Promise.resolve(value.value).then(function(arg) {
                                resume("next", arg);
                            }, function(arg) {
                                resume("throw", arg);
                            });
                        } else {
                            settle(result.done ? "return" : "normal", result.value);
                        }
                    } catch (err) {
                        settle("throw", err);
                    }
                }

                function settle(type, value) {
                    switch (type) {
                        case "return":
                            front.resolve({
                                value: value,
                                done: true
                            });
                            break;

                        case "throw":
                            front.reject(value);
                            break;

                        default:
                            front.resolve({
                                value: value,
                                done: false
                            });
                            break;
                    }

                    front = front.next;

                    if (front) {
                        resume(front.key, front.arg);
                    } else {
                        back = null;
                    }
                }

                this._invoke = send;

                if (typeof gen.return !== "function") {
                    this.return = undefined;
                }
            }

            if (typeof Symbol === "function" && Symbol.asyncIterator) {
                AsyncGenerator.prototype[Symbol.asyncIterator] = function() {
                    return this;
                };
            }

            AsyncGenerator.prototype.next = function(arg) {
                return this._invoke("next", arg);
            };

            AsyncGenerator.prototype.throw = function(arg) {
                return this._invoke("throw", arg);
            };

            AsyncGenerator.prototype.return = function(arg) {
                return this._invoke("return", arg);
            };

            return {
                wrap: function(fn) {
                    return function() {
                        return new AsyncGenerator(fn.apply(this, arguments));
                    };
                },
                await: function(value) {
                    return new AwaitValue(value);
                }
            };
        }();





        var classCallCheck = function(instance, Constructor) {
            if (!(instance instanceof Constructor)) {
                throw new TypeError("Cannot call a class as a function");
            }
        };

        var createClass = function() {
            function defineProperties(target, props) {
                for (var i = 0; i < props.length; i++) {
                    var descriptor = props[i];
                    descriptor.enumerable = descriptor.enumerable || false;
                    descriptor.configurable = true;
                    if ("value" in descriptor) descriptor.writable = true;
                    Object.defineProperty(target, descriptor.key, descriptor);
                }
            }

            return function(Constructor, protoProps, staticProps) {
                if (protoProps) defineProperties(Constructor.prototype, protoProps);
                if (staticProps) defineProperties(Constructor, staticProps);
                return Constructor;
            };
        }();

        var index = (function(_ref) {
            var DOM = _ref.DOM,
                animate = _ref.Animation.animate,
                Extension = _ref.Extension,
                performance = _ref.Date.performance,
                _ref$View = _ref.View,
                rooter = _ref$View.rooter,
                destroyer = _ref$View.destroyer,
                drawer = _ref$View.drawer,
                updater = _ref$View.updater,
                styler = _ref$View.styler;


            var easeOutCubic = Extension.getExtension(Extension.Type.EASING_FUNCTION, 'ease-out-cubic');
            var easeOutSine = Extension.getExtension(Extension.Type.EASING_FUNCTION, 'ease-out-sine');

            var draw = function draw(state) {

                // create cards if not already created
                if (state.isInitialValue()) {

                    // clear current content
                    state.root.textContent = '';

                    // value spacer
                    state.spacer = DOM.create('span', 'tick-flip-spacer');
                    state.root.appendChild(state.spacer);

                    // shaders
                    var shadowTop = DOM.create('span', 'tick-flip-shadow-top tick-flip-shadow tick-flip-front');
                    var shadowBottom = DOM.create('span', 'tick-flip-shadow-bottom tick-flip-shadow tick-flip-back');
                    state.root.appendChild(shadowTop);
                    state.root.appendChild(shadowBottom);

                    // create shadow element
                    state.shadowCard = DOM.create('span', 'tick-flip-card-shadow');
                    state.root.appendChild(state.shadowCard);
                }

                // set spacer value
                state.spacer.textContent = state.value;

                // don't animate when invisible to the user
                if (!state.isInitialValue() && !DOM.visible(state.root)) {
                    state.cards.forEach(function(card) {
                        card.back = state.value;
                        card.front = state.value;
                    });
                    return;
                }

                // get previous card
                var turningCard = state.cards[state.cards.length - 1];
                if (turningCard) {
                    turningCard.waiting = false;
                    turningCard.offset = performance();
                    turningCard.back = state.value;
                }

                // create a quick flipped initial card and then exit
                if (state.isInitialValue()) {

                    // create flipped state (bottom)
                    var initialBottomCard = new FlipCard();
                    initialBottomCard.back = state.value;

                    initialBottomCard.offset = null;
                    initialBottomCard.progress = 1;

                    state.root.insertBefore(initialBottomCard.root, state.root.firstChild);
                    state.cards.push(initialBottomCard);
                }

                // create a new card
                var topCard = new FlipCard();

                topCard.offset = null;
                topCard.progress = 0;
                topCard.visual_progress = 0;
                topCard.waiting = true;

                topCard.front = state.value;
                topCard.rotate(0);
                // topCard.rotate(-1); // prevents slight anti-aliasing issues on Safari / Firefox

                state.root.insertBefore(topCard.root, state.root.firstChild);
                state.cards.push(topCard);

                if (!state.animating) {
                    state.animating = true;

                    var ease = Extension.getExtension(Extension.Type.EASING_FUNCTION, state.style.flipEasing);

                    var tick = function tick() {

                        // find cards that require animation
                        var cardsToAnimate = state.cards.filter(function(card) {
                            return !card.done && !card.waiting;
                        });

                        if (cardsToAnimate.length === 0) {
                            state.animating = false;
                            return;
                        }

                        // calculate card progress
                        cardsToAnimate.forEach(function(card) {

                            if (card.offset !== null) {
                                card.progress = (performance() - card.offset) / state.style.flipDuration;
                            }

                            if (card.progress >= 1) {
                                card.progress = 1;
                                card.done = true;
                            }

                            card.visual_progress = ease(card.progress);
                        });

                        // sort
                        var cardDistance = 0.01;
                        cardsToAnimate.reverse().forEach(function(card, index) {

                            var previousCard = cardsToAnimate[index - 1];

                            if (previousCard && card.visual_progress <= previousCard.visual_progress) {
                                card.visual_progress = previousCard.visual_progress + cardDistance;
                            }
                        });

                        cardsToAnimate.reverse();

                        // update shadows
                        state.cards.forEach(function(card, index) {

                            // set default shadow and highlight levels based on visual animation progress
                            var shadowFrontProgress = 1 - Math.abs(card.visual_progress - .5) * 2;
                            var highlightBackProgress = 1 - (card.visual_progress - .5) / .5;

                            card.shadowFront = shadowFrontProgress;
                            card.highlightBack = highlightBackProgress;

                            // recalculate levels based on other card positions
                            var cardAbove = state.cards[index + 1];

                            // if there's a card above me, my back is visible, and the above card is falling
                            if (cardAbove && card.visual_progress > .5 && card.visual_progress > 0) {
                                card.shadowBack = easeOutCubic(cardAbove.visual_progress);
                            }
                        });

                        // update and animate cards
                        cardsToAnimate.forEach(function(card, index) {

                            var p = card.visual_progress;

                            if (p > .5 && !card.done) {
                                card.root.style.zIndex = 10 + index;
                            } else {
                                card.root.style.removeProperty('z-index');
                            }

                            card.rotate(p * -180);
                        });

                        // handle card stack shadow
                        var shadowProgress = 0;
                        var dist = 1;
                        cardsToAnimate.forEach(function(card) {
                            var d = Math.abs(card.visual_progress - .5);
                            if (d < dist) {
                                dist = d;
                                shadowProgress = card.visual_progress;
                            }
                        });

                        var s = shadowProgress < .5 ? easeOutSine(shadowProgress / .5) : easeOutSine((1 - shadowProgress) / .5);
                        state.shadowCard.style.opacity = s;
                        DOM.transform(state.shadowCard, 'scaleY', s);

                        // clean up cards that finished animating
                        state.cards.filter(function(card) {
                                return card.done;
                            }) // gather all done cards
                            .slice(0, -1) // don't delete the last one
                            .forEach(function(card) {
                                // let's delete them

                                // remove predecessor from cards array
                                state.cards = state.cards.filter(function(c) {
                                    return c !== card;
                                });

                                // remove predecessor from the DOM
                                if (card.root.parentNode) {
                                    state.root.removeChild(card.root);
                                }
                            });

                        requestAnimationFrame(tick);
                    };

                    tick();
                }
            };

            var FlipCard = function() {
                function FlipCard() {
                    classCallCheck(this, FlipCard);


                    this._root = DOM.create('span', 'tick-flip-card');

                    // card front
                    var front = DOM.create('span', 'tick-flip-panel-front tick-flip-front tick-flip-panel');
                    var textFront = DOM.create('span', 'tick-flip-panel-front-text');
                    var textFrontWrapper = DOM.create('span', 'tick-flip-panel-text-wrapper');
                    textFront.appendChild(textFrontWrapper);
                    var shadowFront = DOM.create('span', 'tick-flip-panel-front-shadow');
                    front.appendChild(textFront);
                    front.appendChild(shadowFront);

                    var back = DOM.create('span', 'tick-flip-panel-back tick-flip-back tick-flip-panel');
                    var textBack = DOM.create('span', 'tick-flip-panel-back-text');
                    var textBackWrapper = DOM.create('span', 'tick-flip-panel-text-wrapper');
                    textBack.appendChild(textBackWrapper);
                    var highlightBack = DOM.create('span', 'tick-flip-panel-back-highlight');
                    var shadowBack = DOM.create('span', 'tick-flip-panel-back-shadow');
                    back.appendChild(textBack);
                    back.appendChild(highlightBack);
                    back.appendChild(shadowBack);

                    // create card
                    this._root.appendChild(front);
                    this._root.appendChild(back);

                    // references for animation
                    this._front = front;
                    this._back = back;
                    this._shadowFront = shadowFront;
                    this._shadowBack = shadowBack;
                    this._highlightBack = highlightBack;

                    // back
                    this._textBack = textBackWrapper;
                    this._textFront = textFrontWrapper;

                    // front and back values
                    this._frontValue = null;
                    this._backValue = null;
                }

                createClass(FlipCard, [{
                    key: 'rotate',
                    value: function rotate(degrees) {
                        this._front.style.transform = 'rotateX(' + degrees + 'deg)';
                        this._back.style.transform = 'rotateX(' + (-180 + degrees) + 'deg)';
                    }
                }, {
                    key: 'root',
                    get: function get$$1() {
                        return this._root;
                    }
                }, {
                    key: 'front',
                    set: function set$$1(value) {
                        this._frontValue = value;
                        this._textFront.textContent = value;
                    },
                    get: function get$$1() {
                        return this._frontValue;
                    }
                }, {
                    key: 'back',
                    set: function set$$1(value) {
                        this._backValue = value;
                        this._textBack.textContent = value;
                    },
                    get: function get$$1() {
                        return this._backValue;
                    }
                }, {
                    key: 'highlightBack',
                    set: function set$$1(value) {
                        this._highlightBack.style.opacity = value;
                    }
                }, {
                    key: 'shadowBack',
                    set: function set$$1(value) {
                        this._shadowBack.style.opacity = value;
                    }
                }, {
                    key: 'shadowFront',
                    set: function set$$1(value) {
                        this._shadowFront.style.opacity = value;
                    }
                }]);
                return FlipCard;
            }();

            /**
             * Expose
             */


            return function(root) {

                var state = {

                    cards: [],
                    lastCard: null,
                    initialCard: null,

                    shadowAbove: null,
                    shadowBelow: null,
                    shadowCard: null,

                    currentValue: null,
                    lastValue: null,

                    front: null,
                    back: null
                };

                return Object.assign({}, rooter(state, root, 'flip'), updater(state), styler(state, {
                    flipDuration: 800,
                    flipEasing: 'ease-out-bounce'
                }), drawer(state, draw), destroyer(state));
            };
        });

        module.exports = index;

        module.exports.identifier = {
            name: 'flip',
            type: 'view'
        };
        return module.exports;
    }())]);

}(window));
/* eslint-disable */

/*
 * @pqina/tick v1.8.0 - Counters Made Easy
 * Copyright (c) 2020 PQINA - https://github.com/pqina/tick/
 */
(function(root, plugins, undefined) {
    'use strict';

    // Cut the mustard for really old browsers
    if (!root || !('MutationObserver' in root) || !('requestAnimationFrame' in root)) {
        return;
    }

    // private library reference
    var Tick = (function() {
        if (!module) {
            var module = {};
        }
        'use strict';

        // Available extension types
        var ExtensionType = {
            FONT: 'font',
            VIEW: 'view',
            TRANSFORM: 'transform',
            EASING_FUNCTION: 'easing-function',
            TRANSITION: 'transition'
        };

        // Registered extension collection
        var Extensions = {};
        Extensions[ExtensionType.FONT] = {};
        Extensions[ExtensionType.VIEW] = {};
        Extensions[ExtensionType.TRANSFORM] = {};
        Extensions[ExtensionType.EASING_FUNCTION] = {};
        Extensions[ExtensionType.TRANSITION] = {};

        /**
         * Adds multiple extensions in one go
         * @param type
         * @param extensions
         * @returns {null}
         */
        var addExtensions = function addExtensions(type, extensions) {

            // type does not exist
            if (!Extensions[type]) {
                return null;
            }

            for (var name in extensions) {

                if (!extensions.hasOwnProperty(name)) {
                    continue;
                }

                // name already exists 
                if (Extensions[type][name]) {
                    return null;
                }

                // register
                Extensions[type][name] = extensions[name];
            }
        };

        /**
         * Adds an extension function by type
         * @param type
         * @param name
         * @param fn
         * @returns {null}
         */
        var addExtension = function addExtension(type, name, fn) {

            // type does not exist
            if (!Extensions[type]) {
                throw 'Can\'t add extension with type of "' + type + '", "' + type + '" is not a valid extension type. The following types are valid: ' + keysToList(Extensions);
            }

            // if is invalid name
            if (!/^[-a-z]+$/.test(name)) {
                throw 'Can\'t add extension with name "' + name + '", "' + name + '" is contains invalid characters. Only lowercase alphabetical characters and dashes are allowed.';
            }

            // name in type already exists 
            if (Extensions[type][name]) {
                throw 'Can\'t add extension with name "' + name + '", "' + name + '" is already added.';
            }

            // add
            Extensions[type][name] = fn;
        };

        /**
         * Returns an extension function by name and type
         * @param type
         * @param name
         * @returns {*}
         */
        var getExtension = function getExtension(type, name) {

            // type does not exist
            if (!Extensions[type]) {
                throw 'Can\'t get extension with type of "' + type + '", "' + type + '" is not a valid extension type. The following types are available: ' + keysToList(Extensions);
            }

            // name in type does not exist
            if (!Extensions[type][name]) {
                throw 'Can\'t get extension with name "' + name + '", "' + name + '" is not available. The following extensions are available: ' + keysToList(Extensions[type]);
            }

            return Extensions[type][name];
        };

        var MILLISECOND = 1;
        var SECOND = 1000;
        var MINUTE = 60000;
        var HOUR = 3600000;
        var DAY = 86400000;
        var WEEK = 604800000;
        var MONTH = 2628000000;
        var YEAR = 31536000000;

        var TimeUnit = {
            'Week': WEEK,
            'Day': DAY,
            'Hour': HOUR,
            'Minute': MINUTE,
            'Second': SECOND,
            'Millisecond': MILLISECOND,
            'Month': MONTH,
            'Year': YEAR
        };

        var Months = ['Januari', 'Februari', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

        for (var key in TimeUnit) {
            if (!TimeUnit.hasOwnProperty(key)) {
                continue;
            }
            var val = TimeUnit[key];
            if (val === MILLISECOND) {
                TimeUnit['mi'] = val;
                TimeUnit['ms'] = val;
            } else if (val === MONTH) {
                TimeUnit['M'] = val;
            } else {
                TimeUnit[key.charAt(0).toLowerCase()] = val;
            }
            TimeUnit[key.toLowerCase()] = val;
            TimeUnit[key.toLowerCase() + 's'] = val;
        }

        var Days = {
            Monday: 1,
            Tuesday: 2,
            Wednesday: 3,
            Thursday: 4,
            Friday: 5,
            Saturday: 6,
            Sunday: 0
        };

        var MonthFactor = {
            'M': 1,
            'y': 12
        };

        var serverDate = function serverDate(cb) {

            var xhr = new XMLHttpRequest();

            var now = Date.now();
            xhr.open('HEAD', window.location + '?noCache=' + now);
            xhr.setRequestHeader('Content-Type', 'text/html');
            xhr.setRequestHeader('Cache-Control', 'no-cache');

            xhr.onload = function() {
                var correction = (now - Date.now()) * .5;
                var responseDate = new Date(xhr.getResponseHeader('Date'));
                cb(new Date(responseDate.getTime() + correction));
            };

            xhr.send();
        };

        var isDate = function isDate(date) {
            return date instanceof Date;
        };

        var setTime = function setTime(date, time) {
            date.setHours(time[0] || 0, time[1] || 0, time[2] || 0, time[3] || 0);
            return date;
        };

        var setDay = function setDay(date, day) {
            var current = date.getDay();
            var dist = day - current;
            date.setDate(date.getDate() + dist);
            return date;
        };

        var setDayOfMonth = function setDayOfMonth(date, day) {
            var totalDays = daysInMonth(date.getMonth() + 1, date.getFullYear());
            day = day === 'last' ? totalDays : Math.max(1, Math.min(totalDays, day));
            date.setDate(day);
            return date;
        };

        var setMonth = function setMonth(date, month) {
            date.setMonth(Months.map(function(m) {
                return m.toLowerCase();
            }).indexOf(month));
            return date;
        };

        /*
         Z
         ±hh:mm
         ±hhmm
         ±hh
         */
        var toTimezoneOffset = function toTimezoneOffset(ISO8601Timezone) {
            var current = new Date().getTimezoneOffset() * 60000;
            if (ISO8601Timezone === 'Z') {
                return current;
            }
            var parts = ISO8601Timezone.match(/\+|-|[\d]{2}|[\d]{2}/g);
            var multiplier = parts.shift() === '-' ? -1 : 1;
            var hours = parseInt(parts[0], 10);
            var minutes = parseInt(parts[1], 10);
            // calculate zone offset plus our current zone offset, all in milliseconds
            return multiplier * (hours * 3600000 + minutes * 60000) + current;
        };

        var offsetDate = function offsetDate(offset) {
            return new Date(Date.now() + offset);
        };

        var timezoneDate = function timezoneDate(date, offset) {
            return new Date(date.getTime() + offset);
        };

        // same date (day)
        var sameDate = function sameDate(a, b) {
            return a.toDateString() === b.toDateString();
        };

        // exact same date and time
        var sameTime = function sameTime(a, b) {
            return a.getTime() === b.getTime();
        };

        var daysInMonth = function daysInMonth(month, year) {
            return new Date(year, month, 0).getDate();
        };

        var dateFromISO = function dateFromISO(iso) {

            // use existing timezone
            if (iso.match(/(Z)|([+\-][0-9]{2}:?[0-9]*$)/g)) {
                return new Date(iso);
            }

            // add local timezone
            iso += iso.indexOf('T') !== -1 ? 'Z' : '';
            return dateToLocal(new Date(iso));
        };

        var dateToLocal = function dateToLocal(date) {
            return new Date(date.getTime() + date.getTimezoneOffset() * 60000);
        };

        var timeDuration = function timeDuration(milliseconds, components) {

            return components.map(function(key) {

                var requiredMilliseconds = TimeUnit[key];

                var count = Math.max(0, Math.floor(milliseconds / requiredMilliseconds));

                milliseconds = milliseconds % requiredMilliseconds;

                return count;
            });
        };

        // makes use of time duration for everything expect years and months
        var dateDiff = function dateDiff(a, b, components) {

            // do calculations
            var diff = b - a;
            var swapped = false;
            if (diff < 0) {
                diff = a - b;
                var _ref = [b, a];
                a = _ref[0];
                b = _ref[1];

                swapped = true;
            }

            // set default components
            if (!components) {
                components = ['d', 'h', 'm'];
            }

            // correct month uppercase M if set to lower case
            var mIndex = components.indexOf('m');
            if (mIndex >= 0 && (components[mIndex - 1] === 'y' || components[mIndex + 1] === 'd')) {
                components[mIndex].key = 'M';
            }

            var anchor = void 0;
            var monthsRemaining = void 0;
            var months = void 0;

            var presentsYears = components.includes('y');
            var presentsMonths = components.includes('M');

            if (presentsMonths || presentsYears) {

                anchor = new Date(a.valueOf() + diff);

                monthsRemaining = diffInMonths(anchor, a);

                months = presentsMonths ? Math.floor(monthsRemaining) : Math.floor(monthsRemaining / 12) * 12;

                diff = anchor.valueOf() - addMonths(clone$1(a), months).valueOf();
            }

            var output = components.map(function(key) {

                // if is month or year
                if (key === 'y' || key === 'M') {
                    var _count = Math.max(0, Math.floor(monthsRemaining / MonthFactor[key]));
                    monthsRemaining -= _count * MonthFactor[key];
                    return _count;
                }

                var requiredMilliseconds = TimeUnit[key];

                var count = Math.max(0, Math.floor(diff / requiredMilliseconds));

                diff = diff % requiredMilliseconds;

                return count;
            });

            return swapped ? output.map(function(v) {
                return v > 0 ? -v : v;
            }) : output;
        };

        /**
         * Tick.helper.duration(10, 'seconds') -> milliseconds
         * Tick.helper.duration(a, b, format, cascade) -> [0, 10, 20, 4, 0];
         * @param args
         * @returns {*}
         */
        var duration = function duration() {
            for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
                args[_key] = arguments[_key];
            }

            // if is countdown x amount of milliseconds
            if (typeof args[0] === 'number' && typeof args[1] === 'string') {
                if (!TimeUnit[args[1]]) {
                    throw '"' + args[1] + '" is not a valid amount.';
                }
                return args[0] * TimeUnit[args[1]];
            }

            // is date diff
            if (isDate(args[0])) {
                return dateDiff.apply(undefined, args);
            }

            // is duration in milliseconds
            if (typeof args[0] === 'number' && Array.isArray(args[1])) {
                return timeDuration.apply(undefined, args);
            }

            return null;
        };

        /**
         * Returns current date
         */
        var now$1 = function now() {
            return new Date();
        };

        /**
         * Clones the given date object
         * @param date
         * @returns {Date}
         */
        var clone$1 = function clone(date) {
            return new Date(date.valueOf());
        };

        /**
         * Adds x amount of months to date
         * @param date
         * @param months
         * @returns {*}
         */
        var addMonths = function addMonths(date, months) {
            date.setMonth(date.getMonth() + months);
            return date;
        };

        /**
         * Difference in months between date `a` and date `b`
         * @param a
         * @param b
         * @returns {number}
         */
        var diffInMonths = function diffInMonths(a, b) {

            var wholeMonthDiff = (b.getFullYear() - a.getFullYear()) * 12 + (b.getMonth() - a.getMonth());
            var anchor = addMonths(clone$1(a), wholeMonthDiff);
            var anchor2 = void 0;
            var adjust = void 0;

            if (b - anchor < 0) {
                anchor2 = addMonths(clone$1(a), wholeMonthDiff - 1);
                adjust = (b - anchor) / (anchor - anchor2);
            } else {
                anchor2 = addMonths(clone$1(a), wholeMonthDiff + 1);
                adjust = (b - anchor) / (anchor2 - anchor);
            }

            return -(wholeMonthDiff + adjust);
        };

        /**
         * Destroyer
         * @param state
         */
        var destroyer = (function(state) {

            return {
                destroy: function destroy() {

                    state.destroyed = true;

                    if (state.frame) {
                        cancelAnimationFrame(state.frame);
                    }

                    if (state.styleObserver) {
                        state.styleObserver.disconnect();
                    }

                    if (state.didResizeWindow) {
                        window.removeEventListener('resize', state.didResizeWindow);
                    }

                    if (state.root && state.root.parentNode) {
                        state.root.parentNode.removeChild(state.root);
                    }
                }
            };
        });

        /**
         * Rooter
         * @param state
         * @param root
         * @param name
         */
        var rooter = (function(state) {
            var root = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document.createElement('span');
            var name = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;


            state.root = root;
            state.aligned = null;
            state.destroyed = false;

            if (root && name) {
                state.root.classList.add('tick-' + name);
                state.root.setAttribute('data-view', name);
            }

            if (root && root.dataset.layout) {
                state.align = (root.dataset.layout.match(/left|right|center/) || [])[0] || 'left';
            }

            return {

                appendTo: function appendTo(element) {
                    var location = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'last';


                    // if no root or already attached -> exit
                    if (!state.root || state.root && state.root.parentNode) {
                        return;
                    }

                    if (location === 'last') {
                        // place before last text node if found
                        if (element.childNodes.length && element.childNodes[element.childNodes.length - 1].nodeType === Node.TEXT_NODE) {
                            element.insertBefore(state.root, element.childNodes[element.childNodes.length - 1]);
                        } else {
                            // else just append
                            element.appendChild(state.root);
                        }
                        return;
                    }

                    if (location === 'first') {
                        // no elements and no text
                        if (element.childNodes.length === 0) {
                            element.appendChild(state.root);
                        }
                        // no elements but does contain text
                        else if (element.children.length === 0 && element.childNodes.length) {
                            element.insertBefore(state.root, element.childNodes[element.childNodes.length - 1]);
                        }
                        // elements!
                        else {
                            element.insertBefore(state.root, element.children[0]);
                        }
                    }

                    if (typeof location !== 'string') {
                        element.insertBefore(state.root, location);
                    }
                }

            };
        });

        /**
         * Grouper
         * @param state
         * @param definition
         */
        var grouper = (function(state, definition) {

            state.definition = definition;

            return {
                setDefinition: function setDefinition(definition) {
                    state.definition = definition;
                }
            };
        });

        /**
         * Drawer
         * @param state
         * @param draw
         * @param present
         * @param drawViews
         */
        var drawer = (function(state, _draw, drawViews, present) {

            return {
                draw: function draw() {

                    // not dirty, might need to draw subviews
                    if (!state.dirty) {
                        if (drawViews) {

                            // draw sub views
                            var redrawn = drawViews(state);
                            if (redrawn) {
                                // let's fit it! (if necessary)
                                fit(state);
                            }
                        }
                        return false;
                    }

                    // draw everything
                    _draw(state, present);

                    // let's fit this view (if necessary)
                    fit(state);

                    // no longer dirty
                    state.dirty = false;

                    return true;
                }
            };
        });

        var fit = function fit(state) {

            if (!state.fit) {

                // nope
                if (!state.root || !(state.root.getAttribute('data-layout') || '').match(/fit/)) {
                    state.fit = false;
                    return;
                }

                // create fit info object
                var style = window.getComputedStyle(state.root, null);
                state.fit = true;
                state.fitInfo = {
                    currentFontSize: parseInt(style.getPropertyValue('font-size'), 10)
                };
            }

            // get available width from parent node
            state.fitInfo.availableWidth = state.root.parentNode.clientWidth;

            // the space our target element uses
            state.fitInfo.currentWidth = state.root.scrollWidth;

            // let's calculate the new font size
            var newFontSize = Math.min(Math.max(4, state.fitInfo.availableWidth / state.fitInfo.currentWidth * state.fitInfo.currentFontSize), 1024);

            // size has not changed enough?
            var dist = Math.abs(newFontSize - state.fitInfo.currentFontSize);

            if (dist <= 1) {
                // prevents flickering on firefox / safari / ie by not redrawing tiny font size changes
                return;
            }

            state.fitInfo.currentFontSize = newFontSize;

            state.root.style.fontSize = state.fitInfo.currentFontSize + 'px';
        };

        var updater = (function(state) {

            state.dirty = true;
            state.value = null;
            state.valueUpdateCount = 0;
            state.isInitialValue = function() {
                return state.valueUpdateCount <= 1;
            };

            return {
                reset: function reset() {
                    state.dirty = true;
                    state.value = null;
                    state.valueUpdateCount = 0;
                },
                update: function update(value) {

                    // don't update on same value
                    if (equal(state.value, value)) {
                        return;
                    }

                    state.value = value;
                    state.valueUpdateCount++;
                    state.dirty = true;
                }
            };
        });

        /**
         * Resizer
         * @param state
         */
        var resizer = (function(state) {

            state.didResizeWindow = function() {
                state.dirty = true;
            };

            window.addEventListener('resize', state.didResizeWindow);
        });

        var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) {
            return typeof obj;
        } : function(obj) {
            return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
        };





        var asyncGenerator = function() {
            function AwaitValue(value) {
                this.value = value;
            }

            function AsyncGenerator(gen) {
                var front, back;

                function send(key, arg) {
                    return new Promise(function(resolve, reject) {
                        var request = {
                            key: key,
                            arg: arg,
                            resolve: resolve,
                            reject: reject,
                            next: null
                        };

                        if (back) {
                            back = back.next = request;
                        } else {
                            front = back = request;
                            resume(key, arg);
                        }
                    });
                }

                function resume(key, arg) {
                    try {
                        var result = gen[key](arg);
                        var value = result.value;

                        if (value instanceof AwaitValue) {
                            Promise.resolve(value.value).then(function(arg) {
                                resume("next", arg);
                            }, function(arg) {
                                resume("throw", arg);
                            });
                        } else {
                            settle(result.done ? "return" : "normal", result.value);
                        }
                    } catch (err) {
                        settle("throw", err);
                    }
                }

                function settle(type, value) {
                    switch (type) {
                        case "return":
                            front.resolve({
                                value: value,
                                done: true
                            });
                            break;

                        case "throw":
                            front.reject(value);
                            break;

                        default:
                            front.resolve({
                                value: value,
                                done: false
                            });
                            break;
                    }

                    front = front.next;

                    if (front) {
                        resume(front.key, front.arg);
                    } else {
                        back = null;
                    }
                }

                this._invoke = send;

                if (typeof gen.return !== "function") {
                    this.return = undefined;
                }
            }

            if (typeof Symbol === "function" && Symbol.asyncIterator) {
                AsyncGenerator.prototype[Symbol.asyncIterator] = function() {
                    return this;
                };
            }

            AsyncGenerator.prototype.next = function(arg) {
                return this._invoke("next", arg);
            };

            AsyncGenerator.prototype.throw = function(arg) {
                return this._invoke("throw", arg);
            };

            AsyncGenerator.prototype.return = function(arg) {
                return this._invoke("return", arg);
            };

            return {
                wrap: function(fn) {
                    return function() {
                        return new AsyncGenerator(fn.apply(this, arguments));
                    };
                },
                await: function(value) {
                    return new AwaitValue(value);
                }
            };
        }();





        var classCallCheck = function(instance, Constructor) {
            if (!(instance instanceof Constructor)) {
                throw new TypeError("Cannot call a class as a function");
            }
        };

        var createClass = function() {
            function defineProperties(target, props) {
                for (var i = 0; i < props.length; i++) {
                    var descriptor = props[i];
                    descriptor.enumerable = descriptor.enumerable || false;
                    descriptor.configurable = true;
                    if ("value" in descriptor) descriptor.writable = true;
                    Object.defineProperty(target, descriptor.key, descriptor);
                }
            }

            return function(Constructor, protoProps, staticProps) {
                if (protoProps) defineProperties(Constructor.prototype, protoProps);
                if (staticProps) defineProperties(Constructor, staticProps);
                return Constructor;
            };
        }();







        var _extends = Object.assign || function(target) {
            for (var i = 1; i < arguments.length; i++) {
                var source = arguments[i];

                for (var key in source) {
                    if (Object.prototype.hasOwnProperty.call(source, key)) {
                        target[key] = source[key];
                    }
                }
            }

            return target;
        };



































        var toConsumableArray = function(arr) {
            if (Array.isArray(arr)) {
                for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];

                return arr2;
            } else {
                return Array.from(arr);
            }
        };

        var draw = function draw(state, present) {

            var views = (state.definition || []).concat();

            if (state.align === 'right') {
                views.reverse();
            }

            var value = Array.isArray(state.value) ? state.value.concat() : _typeof(state.value) === 'object' ? clone(state.value) : state.value;

            views.forEach(function(view) {

                if (!view.presenter) {
                    state.update = present(view);
                    if (!view.presenter) {
                        return;
                    }
                    view.presenter.appendTo(state.root);
                }
            });

            views.filter(function(view) {
                return view.presenter !== undefined;
            }).forEach(function(view) {

                if (Array.isArray(value) && state.valueMapping) {
                    // if set to indexes divide values over views, else (must be "none") just pass array
                    state.update(view, state.valueMapping === 'indexes' ? state.align === 'right' ? value.pop() : value.shift() : value);
                } else if (view.key && value[view.key] !== undefined) {
                    // view expects a key so value should be object
                    state.update(view, value[view.key]);
                } else {
                    // just pass on value to all sub views
                    state.update(view, value);
                }
            });

            state.views = views;

            // also draw subviews
            drawViews(state);
        };

        var drawViews = function drawViews(state) {

            var redrawn = false;
            state.views.filter(function(view) {
                return view.presenter !== undefined;
            }).forEach(function(view) {
                if (view.presenter.draw()) {
                    redrawn = true;
                }
            });
            return redrawn;
        };

        var createRoot = (function(root, definition, present) {

            var state = {
                valueMapping: null // "none" or "indexes"
            };

            if (root && root.dataset.valueMapping) {
                var allowed = ['none', 'indexes'];
                var mapping = root.dataset.valueMapping;
                state.valueMapping = allowed.indexOf(mapping) !== -1 ? mapping : null;
            }

            return Object.assign({}, rooter(state, root), resizer(state), updater(state), grouper(state, definition), drawer(state, draw, drawViews, present), destroyer(state));
        });

        var draw$1 = function draw(state, present, ready) {

            // if value is not in form of array force to array
            var value = copyArray(Array.isArray(state.value) ? state.value : (state.value + '').split(''));

            // if we're aligned to the right we will append items differently so view updating is less jumpy
            if (state.align === 'right') {
                value.reverse();
            }

            // clean up presenters if too much presenters
            if (state.definitions.length > value.length) {
                while (state.definitions.length > value.length) {
                    var def = state.definitions.pop();
                    def.presenter.destroy();
                }
            }

            // setup presenters
            value.forEach(function(value, index) {

                var def = state.definitions[index];
                if (!def) {
                    def = state.definitions[index] = cloneDefinition(state.definition);
                    state.update = present(def);
                    def.presenter.appendTo(state.root, state.align === 'right' ? 'first' : 'last');
                }
            });

            // let's update all subs (possibly sets dirty flag)
            value.forEach(function(value, index) {
                return state.update(state.definitions[index], value);
            });

            state.views = value;

            // also draw subviews
            drawViews$1(state);
        };

        var drawViews$1 = function drawViews(state) {

            var redrawn = false;
            state.views.forEach(function(view, index) {
                if (state.definitions[index].presenter.draw()) {
                    redrawn = true;
                }
            });
            return redrawn;
        };

        var createRepeater = (function(root, definition, present) {

            var state = {
                definitions: []
            };

            return Object.assign({}, rooter(state, root), updater(state), grouper(state, definition), drawer(state, draw$1, drawViews$1, present), destroyer(state));
        });

        var VENDOR_PREFIX = typeof document === 'undefined' ? null : function() {
            var VENDORS = ['webkit', 'Moz', 'ms', 'O'];
            var i = 0;
            var l = VENDORS.length;
            var transform = void 0;
            var elementStyle = document.createElement('div').style;
            for (; i < l; i++) {
                transform = VENDORS[i] + 'Transform';
                if (transform in elementStyle) {
                    return VENDORS[i];
                }
            }
            return null;
        }();

        var text = function text(node, value) {
            var textNode = node.childNodes[0];
            if (!textNode) {
                textNode = document.createTextNode(value);
                node.appendChild(textNode);
            } else if (value !== textNode.nodeValue) {
                textNode.nodeValue = value;
            }
        };

        var create$1 = function create(name, className) {
            var el = document.createElement(name);
            if (className) {
                el.className = className;
            }
            return el;
        };

        var observeAttributes = function observeAttributes(element, attributes, cb) {
            var observer = new MutationObserver(function(mutations) {
                attributes.forEach(function(attr) {
                    if (mutations.filter(function(mutation) {
                            return attributes.includes(mutation.attributeName);
                        }).length) {
                        cb(element.getAttribute(attr));
                    }
                });
            });
            observer.observe(element, {
                attributes: true
            });
            return observer;
        };

        var isHTMLElement = function isHTMLElement(value) {
            return value instanceof HTMLElement;
        };

        /**
         * Element Transform Origin
         * @param element
         * @param value
         */
        var setTransformOrigin = function setTransformOrigin(element, value) {
            element.style.transformOrigin = value;
        };

        /**
         * Element Transforms
         * @param element
         * @param name
         * @param value
         * @param unit
         */
        var setTransform = function setTransform(element, name, value) {
            var unit = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '';


            if (!element.transforms) {
                element.transforms = [];
            }

            var t = element.transforms.find(function(t) {
                return t.name === name;
            });
            if (t) {
                t.value = value;
            } else {
                element.transforms.push({
                    name: name,
                    value: value,
                    unit: unit
                });
            }

            setTransformStyle(element, element.transforms);
        };

        var setTransformStyle = function setTransformStyle(element, transforms) {
            element.style.transform = transforms.map(function(t) {
                return t.name + '(' + t.value + t.unit + ')';
            }).join(' ');
        };

        var isVisible = function isVisible(element) {

            var elementRect = element.getBoundingClientRect();

            // is above top of the page
            if (elementRect.bottom < 0) {
                return false;
            }

            // is below bottom of page
            if (elementRect.top > window.scrollY + window.innerHeight) {
                return false;
            }

            return true;
        };

        /**
         * @param value { * }
         */
        var toBoolean$1 = function toBoolean(value) {
            return typeof value === 'string' ? value === 'true' : value;
        };

        /**
         * @param string { string }
         */
        var capitalizeFirstLetter$1 = function capitalizeFirstLetter(string) {
            return string.charAt(0).toUpperCase() + string.slice(1);
        };

        /**
         * @param string { string }
         */
        var trim$1 = function trim(string) {
            return string.trim();
        };

        var CACHE = {};

        var cache = (function(value, fn) {
            var fns = fn.toString();
            if (!CACHE[fns]) {
                CACHE[fns] = {};
            }
            if (!CACHE[fns][value]) {
                CACHE[fns][value] = fn(value);
            }
            return CACHE[fns][value];
        });

        var isInt = new RegExp('^[0-9]+$');
        var isBoolean$1 = new RegExp('^(true|false)$');
        var isFloat = new RegExp('^[0-9.]+$');
        var isColor = new RegExp('color');
        var isShadow = new RegExp('shadow');
        var isGradient = new RegExp('^(follow-gradient|horizontal-gradient|vertical-gradient)');
        var isDuration = new RegExp('^[.0-9]+(?:ms|s){1}$');
        var isTransition = new RegExp('^transition-?(?:in|out)?$');
        var isURL = new RegExp('^url\\(');

        var toDuration = function toDuration(string) {
            return string ? parseFloat(string) * (/ms$/.test(string) ? 1 : 1000) : 0;
        };

        var toTransition = function toTransition(string) {
            return string.match(/[a-z]+(?:\(.*?\))?\s?(?:origin\(.*?\))?\s?(?:[a-z]+\(.*?\))?[ .a-z-0-9]*/g).map(toTransitionPartial);
        };

        var toTransitionPartial = function toTransitionPartial(string) {

            var parts = string.match(/([a-z]+(?:\(.*?\))?)\s?(?:origin\((.*?)\))?\s?([a-z]+(?:\(.*?\))?)?\s?(?:([.0-9ms]+)?\s?(?:(ease-[a-z-]+))?\s?([.0-9ms]+)?)?/);

            // get transition function definition
            var fn = toFunctionOutline(parts[1]);

            // get duration and easing
            var origin = undefined;
            var duration = undefined;
            var ease = undefined;
            var delay = undefined;
            var resolver = undefined;

            // skip function and figure out what other parts are
            parts.slice(2).filter(function(part) {
                return typeof part !== 'undefined';
            }).forEach(function(part) {

                // is either duration or delay
                if (isDuration.test(part)) {
                    if (typeof duration === 'undefined') {
                        duration = toDuration(part);
                    } else {
                        delay = toDuration(part);
                    }
                }

                // is origin if contains a space
                else if (/ /.test(part)) {
                    origin = part;
                }

                // should be ease
                else if (/^ease-[a-z-]+$/.test(part)) {
                    ease = part;
                }

                // should be transform
                else if (/^[a-z]+/.test(part)) {
                    resolver = toFunctionOutline(part);
                }
            });

            // reset easing and duration when transform is defined, these settings don't work together
            if (resolver) {
                duration = undefined;
                ease = undefined;
            }

            // return transition object
            return {
                name: fn.name,
                parameters: fn.parameters,
                duration: duration,
                ease: ease,
                delay: delay,
                origin: origin,
                resolver: resolver
            };
        };

        /**
         * toGradient
         * @param string { string } - string should be in format <type>(color, color)
         * @returns { {type: *, colors: *} }
         */
        var toGradient = function toGradient(string) {
            var type = string.match(/follow-gradient|horizontal-gradient|vertical-gradient/)[0];
            var colors = string.substr(type.length).match(/(?:transparent|rgb\(.*?\)|hsl\(.*?\)|hsla\(.*?\)|rgba\(.*?\)|[a-z]+|#[abcdefABCDEF\d]+)\s?(?:[\d]{1,3}%?)?/g).map(toGradientColor);
            return {
                type: type,
                colors: colors
            };
        };

        var gradientOffsetRegex = /\s([\d]{1,3})%?$/;
        var toGradientColor = function toGradientColor(string) {
            var offset = string.match(gradientOffsetRegex);
            return {
                offset: offset ? parseFloat(offset[1]) / 100 : null,
                value: toColor(string.replace(gradientOffsetRegex, ''))
            };
        };

        /**
         * Returns the pixels amount for the given value
         */
        var pipetteCache = [];

        var getPipette = function getPipette(id, root) {
            if (!pipetteCache[id]) {
                return null;
            }
            return pipetteCache[id].find(function(p) {
                return p.node.parentNode === root;
            });
        };

        var setPipette = function setPipette(id, pipette) {
            if (!pipetteCache[id]) {
                pipetteCache[id] = [];
            }
            pipetteCache[id].push(pipette);
        };

        var toPixels = typeof document === 'undefined' ? function(value) {
            return 0;
        } : function(value) {
            var root = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document.body;
            var id = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;


            if (value == 0) {
                return 0;
            }

            if (id) {

                var _pipette = getPipette(id, root) || {};
                if (!_pipette.node) {
                    _pipette.node = document.createElement('span');
                    _pipette.node.style.cssText = 'position:absolute;padding:0;visibility:hidden;';
                    root.appendChild(_pipette.node);
                }

                // update value
                _pipette.node.style.marginTop = value;

                // compute style for first time
                if (!_pipette.style) {
                    _pipette.style = window.getComputedStyle(_pipette.node);
                }

                setPipette(id, _pipette);

                return parseInt(_pipette.style.marginTop, 10);
            }

            // old method
            var pipette = document.createElement('span');
            pipette.style.cssText = 'position:absolute;padding:0;visibility:hidden;margin-top:' + value;
            root.appendChild(pipette);
            requestAnimationFrame(function() {
                pipette.parentNode.removeChild(pipette);
            });
            return parseInt(window.getComputedStyle(pipette).marginTop, 10);
        };

        /**
         * @param string { string } - any valid CSS color value
         * @returns { string }
         */
        var toColor = typeof document === 'undefined' ? function(string) {
            return string;
        } : function(string) {
            if (string === 'transparent') {
                return 'rgba(0,0,0,0)';
            }
            var pipette = document.createElement('span');
            pipette.style.cssText = 'position:absolute;visibility:hidden;color:' + string;
            document.body.appendChild(pipette);
            requestAnimationFrame(function() {
                pipette.parentNode.removeChild(pipette);
            });
            return window.getComputedStyle(pipette).getPropertyValue('color');
        };

        var toShadow = function toShadow(style) {

            if (typeof style !== 'string') {
                return style;
            }

            return style.match(/([-.\d]+(?:%|ms|s|deg|cm|em|ch|ex|q|in|mm|pc|pt|px|vh|vw|vmin|vmax)?)|[%#A-Za-z0-9,.()]+/g);
        };

        var toURL = function toURL(style) {
            var urls = style.match(/url\((.*?)\)/g).map(function(url) {
                return url.substring(4, url.length - 1);
            });
            return urls.length === 1 ? urls[0] : urls;
        };

        var toStyleProperty = function toStyleProperty(key) {
            return key.trim().split('-').map(function(key, index) {
                return index > 0 ? capitalizeFirstLetter$1(key) : key;
            }).join('');
        };

        var toStyleValue = function toStyleValue(value, property) {

            if (isBoolean$1.test(value)) {
                return toBoolean$1(value);
            }

            if (isInt.test(value)) {
                return parseInt(value, 10);
            }

            if (isFloat.test(value)) {
                return parseFloat(value);
            }

            if (isURL.test(value)) {
                return toURL(value);
            }

            if (isColor.test(property)) {
                if (isGradient.test(value)) {
                    return cache(value, toGradient);
                }
                return cache(value, toColor);
            }

            if (isShadow.test(property)) {
                return cache(value, toShadow);
            }

            if (isTransition.test(property)) {
                if (value === 'none') {
                    return value;
                }
                return cache(value, toTransition);
            }

            return value;
        };

        var toStyle = function toStyle(string) {
            var parts = string.split(':').map(trim$1);
            var property = toStyleProperty(parts[0]);
            var value = toStyleValue(parts[1], parts[0]);
            if (!property || value === null || typeof value === 'undefined') {
                return null;
            }
            return {
                property: property,
                value: value
            };
        };

        var toStyles = function toStyles(string) {
            return string.split(';')

                // remove empty values
                .filter(function(style) {
                    return style.trim().length;
                })

                // turn into objects
                .map(toStyle)

                // remove invalid styles
                .filter(function(style) {
                    return style !== null;
                })

                // create styles object
                .reduce(function(styles, style) {
                    styles[style.property] = style.value;
                    return styles;
                }, {});
        };

        // https://gist.github.com/gre/1650294
        // http://easings.net/
        // https://github.com/danro/jquery-easing/blob/master/jquery.easing.js
        // http://gizma.com/easing/

        var easeLinear = function easeLinear(t) {
            return t;
        };

        var easeInSine = function easeInSine(t) {
            return -1 * Math.cos(t * (Math.PI / 2)) + 1;
        };

        var easeOutSine = function easeOutSine(t) {
            return Math.sin(t * (Math.PI / 2));
        };

        var easeInOutSine = function easeInOutSine(t) {
            return -0.5 * (Math.cos(Math.PI * t) - 1);
        };

        var easeInQuad = function easeInQuad(t) {
            return t * t;
        };

        var easeOutQuad = function easeOutQuad(t) {
            return t * (2 - t);
        };

        var easeInOutQuad = function easeInOutQuad(t) {
            return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
        };

        var easeInCubic = function easeInCubic(t) {
            return t * t * t;
        };

        var easeOutCubic = function easeOutCubic(t) {
            var t1 = t - 1;
            return t1 * t1 * t1 + 1;
        };

        var easeInOutCubic = function easeInOutCubic(t) {
            return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
        };

        var easeInQuart = function easeInQuart(t) {
            return t * t * t * t;
        };

        var easeOutQuart = function easeOutQuart(t) {
            return 1 - --t * t * t * t;
        };

        var easeInOutQuart = function easeInOutQuart(t) {
            return t < .5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
        };

        var easeInExpo = function easeInExpo(t) {
            if (t === 0) {
                return 0;
            }
            return Math.pow(2, 10 * (t - 1));
        };

        var easeOutExpo = function easeOutExpo(t) {
            if (t === 1) {
                return 1;
            }
            return -Math.pow(2, -10 * t) + 1;
        };

        var easeInOutExpo = function easeInOutExpo(t) {

            if (t === 0 || t === 1) {
                return t;
            }

            var scaledTime = t * 2;
            var scaledTime1 = scaledTime - 1;

            if (scaledTime < 1) {
                return 0.5 * Math.pow(2, 10 * scaledTime1);
            }

            return 0.5 * (-Math.pow(2, -10 * scaledTime1) + 2);
        };

        var easeInCirc = function easeInCirc(t) {
            var scaledTime = t / 1;
            return -1 * (Math.sqrt(1 - scaledTime * t) - 1);
        };

        var easeOutCirc = function easeOutCirc(t) {
            var t1 = t - 1;
            return Math.sqrt(1 - t1 * t1);
        };

        var easeInOutCirc = function easeInOutCirc(t) {

            var scaledTime = t * 2;
            var scaledTime1 = scaledTime - 2;

            if (scaledTime < 1) {
                return -0.5 * (Math.sqrt(1 - scaledTime * scaledTime) - 1);
            }

            return 0.5 * (Math.sqrt(1 - scaledTime1 * scaledTime1) + 1);
        };

        var easeInBack = function easeInBack(t) {
            var magnitude = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1.70158;

            var scaledTime = t / 1;
            return scaledTime * scaledTime * ((magnitude + 1) * scaledTime - magnitude);
        };

        var easeOutBack = function easeOutBack(t) {
            var magnitude = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1.70158;

            var scaledTime = t / 1 - 1;
            return scaledTime * scaledTime * ((magnitude + 1) * scaledTime + magnitude) + 1;
        };

        var easeInOutBack = function easeInOutBack(t) {
            var magnitude = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1.70158;

            var scaledTime = t * 2;
            var scaledTime2 = scaledTime - 2;
            var s = magnitude * 1.525;
            if (scaledTime < 1) {
                return 0.5 * scaledTime * scaledTime * ((s + 1) * scaledTime - s);
            }

            return 0.5 * (scaledTime2 * scaledTime2 * ((s + 1) * scaledTime2 + s) + 2);
        };

        var easeOutElastic = function easeOutElastic(t) {
            var magnitude = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.7;


            var p = 1 - magnitude;
            var scaledTime = t * 2;

            if (t === 0 || t === 1) {
                return t;
            }

            var s = p / (2 * Math.PI) * Math.asin(1);
            return Math.pow(2, -10 * scaledTime) * Math.sin((scaledTime - s) * (2 * Math.PI) / p) + 1;
        };

        var easeOutBounce = function easeOutBounce(t) {
            var scaledTime = t / 1;
            if (scaledTime < 1 / 2.75) {
                return 7.5625 * scaledTime * scaledTime;
            } else if (scaledTime < 2 / 2.75) {
                var scaledTime2 = scaledTime - 1.5 / 2.75;
                return 7.5625 * scaledTime2 * scaledTime2 + 0.75;
            } else if (scaledTime < 2.5 / 2.75) {
                var _scaledTime = scaledTime - 2.25 / 2.75;
                return 7.5625 * _scaledTime * _scaledTime + 0.9375;
            } else {
                var _scaledTime2 = scaledTime - 2.625 / 2.75;
                return 7.5625 * _scaledTime2 * _scaledTime2 + 0.984375;
            }
        };

        var EasingFunctions = {
            'ease-linear': easeLinear,

            'ease-in-sine': easeInSine,
            'ease-out-sine': easeOutSine,
            'ease-in-out-sine': easeInOutSine,

            'ease-in-cubic': easeInCubic,
            'ease-out-cubic': easeOutCubic,
            'ease-in-out-cubic': easeInOutCubic,

            'ease-in-circ': easeInCirc,
            'ease-out-circ': easeOutCirc,
            'ease-in-out-circ': easeInOutCirc,

            'ease-in-quad': easeInQuad,
            'ease-out-quad': easeOutQuad,
            'ease-in-out-quad': easeInOutQuad,

            'ease-in-quart': easeInQuart,
            'ease-out-quart': easeOutQuart,
            'ease-in-out-quart': easeInOutQuart,

            'ease-in-expo': easeInExpo,
            'ease-out-expo': easeOutExpo,
            'ease-in-out-expo': easeInOutExpo,

            'ease-in-back': easeInBack,
            'ease-out-back': easeOutBack,
            'ease-in-out-back': easeInOutBack,

            'ease-out-elastic': easeOutElastic,
            'ease-out-bounce': easeOutBounce
        };

        addExtensions(ExtensionType.EASING_FUNCTION, EasingFunctions);

        /**
         * animate a certain amount of time (between 0 and 1)
         * @param cb - update function
         * @param complete
         * @param duration - duration in milliseconds
         * @param ease - easing function
         * @param delay
         */
        var animate = function animate(cb, complete) {
            var duration = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 500;
            var ease = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : easeLinear;
            var delay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;

            return interpolate(function(t) {
                cb(ease(t));
            }, complete, duration, delay);
        };

        /**
         * interpolate between 0 and 1 over x amount of frames
         * @param update - update function
         * @param complete
         * @param duration - duration in milliseconds
         * @param delay - milliseconds to wait before starting
         */
        var interpolate = function interpolate(update) {
            var complete = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
            var duration = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 500;
            var delay = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;

            // no update function supplied -> exit
            if (!update) {
                return null;
            }

            // set start time
            var start = null;
            var t = void 0;
            var frame = null;

            // animation loop
            var tick = function tick(ts) {
                if (start === null) {
                    start = ts;
                }

                t = ts - start - delay;

                if (t < duration) {
                    update(t >= 0 ? t / duration : 0);
                    frame = requestAnimationFrame(tick);
                    return null;
                }

                update(1);

                if (complete) {
                    complete();
                }
            };

            tick(now());

            // return cancel function
            return function() {
                cancelAnimationFrame(frame);
            };
        };

        /**
         * Translates movements values
         */
        var translator = function translator() {
            var fps = 24;
            var interval = 1000 / fps;

            var frame = null;

            var state = {
                velocity: 0,
                origin: 0,
                position: 0,
                destination: 1
            };

            var cancel = function cancel() {
                cancelAnimationFrame(frame);
            };

            var translate = function translate(cb, from, to, update) {
                // cancel previous animations if are running
                cancel();

                // 'to' not supplied, so 'from' is destination
                if (to === null) {
                    state.destination = from;
                } else {
                    // both supplied, also reset velocity
                    state.position = from;
                    state.destination = to;
                    state.velocity = 0;
                }

                // always set origin to current position
                state.origin = state.position;

                var last = null;
                var tick = function tick(ts) {
                    // queue next tick
                    frame = requestAnimationFrame(tick);

                    // limit fps
                    if (!last) {
                        last = ts;
                    }

                    var delta = ts - last;

                    if (delta <= interval) {
                        // skip frame
                        return;
                    }

                    // align next frame
                    last = ts - delta % interval;

                    update(state, cancel);

                    cb(state.position);
                };

                tick(now());
            };

            return {
                getPosition: function getPosition() {
                    return state.position;
                },
                cancel: cancel,
                translate: translate
            };
        };

        /**
         * Translator builder
         * @param type
         * @param options
         * @returns {*}
         */
        var createTranslator = function createTranslator(type) {
            for (var _len = arguments.length, options = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
                options[_key - 1] = arguments[_key];
            }

            var t = translator();
            var updater = {
                update: null,
                cancel: t.cancel,
                getPosition: t.getPosition
            };

            if (type === 'arrive') {
                updater.update = arrive.apply(undefined, [t.translate].concat(options));
            } else if (type === 'spring') {
                updater.update = spring.apply(undefined, [t.translate].concat(options));
            } else if (type === 'step') {
                updater.update = step.apply(undefined, [t.translate].concat(options));
            }

            return updater;
        };

        /**
         * Arrive at destination
         * @param update
         * @param maxVelocity
         * @param friction
         */
        var arrive = function arrive(update) {
            var maxVelocity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
            var friction = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0.01;

            return function(cb) {
                var from = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
                var to = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;

                update(cb, from, to, function(state, cancel) {
                    // distance to target
                    var distance = state.destination - state.position;
                    var halfway = state.origin + (state.destination - state.origin) * 0.5;

                    // update velocity based on distance
                    state.velocity += (-(halfway - state.origin) + distance) * 2 * friction;

                    // update position by adding velocity
                    state.position += state.velocity < 0 ? Math.max(state.velocity, -maxVelocity) : Math.min(state.velocity, maxVelocity);

                    // we've arrived if we're near target and our velocity is near zero
                    if (state.origin < state.destination && state.position >= state.destination || state.origin >= state.destination && state.position <= state.destination) {
                        cancel();
                        state.velocity = 0;
                        state.position = state.destination;
                    }
                });
            };
        };

        var step = function step(update) {
            var velocity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.01;

            return function(cb) {
                var from = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
                var to = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;

                update(cb, from, to, function(state, cancel) {
                    // update velocity based on distance
                    state.velocity = velocity;

                    // update position by adding velocity
                    state.position += state.velocity;

                    // we've arrived if we're near target and our velocity is near zero
                    if (state.origin < state.destination && state.position >= state.destination || state.origin >= state.destination && state.position <= state.destination) {
                        cancel();
                        state.velocity = 0;
                        state.position = state.destination;
                    }
                });
            };
        };

        /**
         * Animate movement based no spring physics
         * @param update
         * @param stiffness - the higher the more intense the vibration
         * @param damping - a factor that slows down the calculated velocity by a percentage, needs to be less than 1
         * @param mass - the higher the slower the spring springs in action
         * @returns {function(*=, *=)}
         */
        var spring = function spring(update) {
            var stiffness = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.5;
            var damping = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0.75;
            var mass = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 10;

            return function(cb) {
                var from = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
                var to = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;

                update(cb, from, to, function(state, cancel) {
                    // calculate spring force
                    var f = -(state.position - state.destination) * stiffness;

                    // update velocity by adding force based on mass
                    state.velocity += f / mass;

                    // update position by adding velocity
                    state.position += state.velocity;

                    // slow down based on amount of damping
                    state.velocity *= damping;

                    // we've arrived if we're near target and our velocity is near zero
                    if (thereYet(state.position, state.destination, state.velocity)) {
                        cancel();
                        state.position = state.destination;
                        state.velocity = 0;
                    }
                });
            };
        };

        var thereYet = function thereYet(position, destination, velocity) {
            var errorMargin = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0.001;

            return Math.abs(position - destination) < errorMargin && Math.abs(velocity) < errorMargin;
        };

        /**
         * Returns a function that applies the transitions to the given element
         * @param transitions
         * @returns {function(*)}
         */
        var createTransitioner = function createTransitioner(transitions) {

            var transitioners = transitions.map(function(t) {
                return createDurationTransitioner(createTransition(t.name, t.parameters, t.ease), t.origin, t.duration, t.delay);
            });

            return function(element, direction, complete) {

                // don't run animations when no element is supplied
                if (!isHTMLElement(element)) {
                    return false;
                }

                var count = transitioners.length;
                transitioners.forEach(function(transitioner) {
                    transitioner(element, direction, function() {
                        count--;
                        if (!count && complete) {
                            complete(element);
                        }
                    });
                });
            };
        };

        var createTransition = function createTransition(name, parameters, ease) {
            var easing = ease ? getExtension(ExtensionType.EASING_FUNCTION, ease) : ease;
            var transition = getExtension(ExtensionType.TRANSITION, name);
            return function(element, direction, p) {
                transition.apply(undefined, [element, p, direction, easing].concat(toConsumableArray(parameters)));
            };
        };

        var createDurationTransitioner = function createDurationTransitioner(transition) {
            var origin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '50% 50% 0';
            var duration = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 500;
            var delay = arguments[3];


            return function(element) {
                var direction = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
                var complete = arguments[2];


                // set transform origin
                setTransformOrigin(element, origin);

                // run animation
                interpolate(function(p) {
                    transition(element, direction, p);
                }, complete, duration, delay);
            };
        };

        var getComposedTransitionActs = function getComposedTransitionActs(transition) {
            return getExtension(ExtensionType.TRANSITION, transition.name).apply(undefined, toConsumableArray(transition.parameters || []));
        };

        /**
         * Styler
         * @param state
         * @param base
         */
        var styler = (function(state) {
            var base = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};


            // styles that were last applied to the element
            state.lastAppliedStyles = null;

            // set default style
            updateStyles(state, base, state.root.dataset.style);

            // setup observer, will observe style attribute so can restyle when changed
            state.styleObserver = observeAttributes(state.root, ['data-style'], function(string) {
                updateStyles(state, base, string);
            });

            // adds style setter
            return {
                setStyle: function setStyle(css) {
                    updateStyles(state, base, css);
                }
            };
        });

        var updateStyles = function updateStyles(state, base, css) {

            // don't update if is same
            if (state.lastAppliedStyles === css) {
                return;
            }

            // remember these styles
            state.lastAppliedStyles = css;

            state.style = css ? mergeObjects(base, toStyles(css)) : base;

            var intro = [];
            var outro = [];

            if (state.style.transitionIn && state.style.transitionIn.length) {
                intro = state.style.transitionIn;
                outro = state.style.transitionOut;
            } else if (state.style.transition && state.style.transition !== 'none') {

                state.style.transition.forEach(function(transition) {
                    var acts = getComposedTransitionActs(transition);
                    intro = intro.concat(acts.intro);
                    outro = outro.concat(acts.outro);
                });
            }

            if (intro && outro) {
                state.transitionIn = createTransitioner(intro);
                state.transitionOut = createTransitioner(outro);

                state.skipToTransitionInEnd = createTransitioner(intro.map(clearTiming));
                state.skipToTransitionOutEnd = createTransitioner(outro.map(clearTiming));
            }

            state.dirty = true;
        };

        var clearTiming = function clearTiming(t) {
            var tn = clone(t);
            tn.duration = 0;
            tn.delay = 0;
            return tn;
        };

        var getBackingStoreRatio = function getBackingStoreRatio(ctx) {
            return ctx[VENDOR_PREFIX + 'BackingStorePixelRatio'] || ctx.backingStorePixelRatio || 1;
        };

        var getDevicePixelRatio = function getDevicePixelRatio() {
            return window.devicePixelRatio || 1;
        };

        var clearCanvas = function clearCanvas(canvas) {
            var ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, canvas.width, canvas.height);
        };

        /**
         * View Composers
         */
        /**
         * API Utilities
         */
        /**
         * Add default text view
         */
        var Views = {
            'text': function text$$1() {
                return function(root) {

                    var state = {};

                    var draw = function draw(state) {
                        state.root.setAttribute('data-value', state.value);
                        text(state.root, state.value);
                    };

                    return Object.assign({}, rooter(state, root, 'text'), updater(state), drawer(state, draw), destroyer(state));
                };
            }

        };

        addExtensions(ExtensionType.VIEW, Views);

        /**
         * Internal API for use by views
         */
        var API$2 = function API() {
            return {
                Extension: {
                    Type: ExtensionType,
                    getExtension: getExtension
                },
                Utils: {
                    toPixels: toPixels,
                    toColor: toColor
                },
                Canvas: {
                    clear: clearCanvas,
                    getDevicePixelRatio: getDevicePixelRatio,
                    getBackingStoreRatio: getBackingStoreRatio
                },
                DOM: {
                    visible: isVisible,
                    create: create$1,
                    transform: setTransform
                },
                Animation: {
                    animate: animate
                },
                Data: {
                    request: request
                },
                Date: {
                    performance: now
                },
                View: {
                    rooter: rooter,
                    drawer: drawer,
                    updater: updater,
                    styler: styler,
                    grouper: grouper,
                    resizer: resizer,
                    destroyer: destroyer
                }
            };
        };

        /**
         * Base view definitions
         */
        var createPresenterRoot = function createPresenterRoot(root, definition, presentDefinition) {
            return createRoot(root, definition, presentDefinition);
        };

        var createPresenterRepeater = function createPresenterRepeater(root, definition, presentDefinition) {
            return createRepeater(root, definition, presentDefinition);
        };

        var createPresenterView = function createPresenterView(name, root, style) {
            var view = getExtension(ExtensionType.VIEW, name);
            return view ? view(API$2())(root, style) : null;
        };

        var arrow = function arrow(str, i) {
            return str[i] === '-' && str[i + 1] === '>';
        };

        var string = function string(c) {
            return c === "'" || c === '"';
        };

        var comma = function comma(c) {
            return c === ',';
        };

        var opener = function opener(c) {
            return c === '(';
        };

        var closer = function closer(c) {
            return c === ')';
        };

        var value = function value(v) {
            return v.trim().length !== 0;
        };

        var add = function add(r, v) {
            return r.push(v.trim());
        };

        var token = function token(r, v) {
            if (value(v)) {
                add(r, v);
                return '';
            }
            return v;
        };

        var chain = function chain(_chain, output) {
            if (_chain.length) {
                output.push(_chain.length > 1 ? _chain.concat() : _chain[0]);
            }
            return [];
        };

        var parse$1 = function parse(i, str, result) {

            var v = '';
            var fns = [];
            var quote = null;
            var hitArrow = false;

            while (i < str.length) {

                // character reference
                var c = str[i];

                // enter level
                if (opener(c)) {

                    hitArrow = false;
                    var fn = [v.trim()];

                    i = parse(i + 1, str, fn);
                    c = str[i];

                    fns.push(fn);
                    v = '';
                }

                // exit level
                else if (closer(c)) {

                    if (hitArrow && v.trim().length) {
                        fns.push([v.trim()]);
                        v = '';
                        hitArrow = false;
                    }

                    if (value(v)) {
                        add(fns, v);
                    }

                    fns = chain(fns, result);

                    return i + 1;
                }

                // function names or arguments
                else {

                    // we're in a string
                    // as long as the exit has not been found add to value
                    if (quote !== null && c !== quote) {

                        // accept any value
                        v += c;
                    }
                    // we've found the string exit
                    else if (c === quote) {

                        fns.push(v);
                        v = '';

                        quote = null;
                    }
                    // we're not in a string and we found a string opener
                    else if (string(c)) {
                        v = '';
                        quote = c;
                    } else {

                        // we're not in a string

                        // we've found an arrow
                        if (arrow(str, i)) {

                            hitArrow = true;

                            // we might have finished a function without parenthesis
                            if (v.trim().length) {
                                fns.push([v.trim()]);
                                v = '';
                            }

                            // skip two additional characters because arrow is of length 2
                            i += 2;
                        }
                        // we've reached an argument separator
                        else if (comma(c)) {

                            if (hitArrow && v.trim().length) {
                                fns.push([v.trim()]);
                                v = '';
                                hitArrow = false;
                            }

                            fns = chain(fns, result);

                            // add possible previous token
                            v = token(result, v);
                        } else {
                            v += c;
                        }
                    }

                    // next character
                    i++;
                }
            }

            if (hitArrow && v.trim().length || !hitArrow && v.trim().length && !fns.length) {
                fns.push([v.trim()]);
                v = '';
            }

            chain(fns, result);

            // add final token
            token(result, v);

            return i;
        };

        var parseTransformChain = function parseTransformChain(string) {
            var result = [];
            parse$1(0, string, result);
            return result;
        };

        var isRootDefinition = function isRootDefinition(definition) {
            return definition.children && definition.children.length;
        };

        var cloneDefinition = function cloneDefinition(definition) {

            var clone = {};

            for (var key in definition) {

                if (!definition.hasOwnProperty(key)) {
                    continue;
                }

                if (key === 'root') {
                    clone[key] = definition[key].cloneNode();
                    continue;
                }

                if (key === 'children') {
                    clone[key] = definition[key] === null ? null : definition[key].map(cloneDefinition);
                    continue;
                }

                if (key === 'repeat') {
                    clone[key] = definition[key] === null ? null : cloneDefinition(definition[key]);
                    continue;
                }

                clone[key] = definition[key];
            }

            clone.presenter = null;

            return clone;
        };

        /**
         * Parsing DOM to DefinitionTree
         * @param nodes
         */
        var definitionOutline = {
            root: null,
            key: null,
            view: null,
            overlay: null,
            presenter: null,
            transform: null,
            layout: null,
            style: null,
            repeat: null,
            children: null,
            className: null
        };

        var toPresenterDefinitionTree = function toPresenterDefinitionTree(nodes) {
            return Array.from(nodes)
                // fix to allow nesting of tick counters
                // .filter(node => !node.classList.contains('tick'))
                .map(function(node) {

                    var definition = mergeObjects(definitionOutline, {
                        root: node
                    });

                    // get all properties above from attributes
                    for (var key in node.dataset) {
                        if (!node.dataset.hasOwnProperty(key)) {
                            continue;
                        }
                        if (typeof definition[key] === 'undefined') {
                            continue;
                        }
                        definition[key] = node.dataset[key];
                    }

                    // if is repeater set do not parse children as children but define as repeat
                    if (definition.repeat) {

                        // can only have one repeated child
                        definition.repeat = toPresenterDefinitionTree(node.children).pop();

                        // detach repeated elements from DOM
                        Array.from(node.children).forEach(function(node) {
                            node.parentNode.removeChild(node);
                        });
                    }
                    // children are normal children
                    else if (node.children.length) {
                        definition.children = toPresenterDefinitionTree(node.children);
                    }

                    return definition;
                });
        };

        var createDOMTreeForDefinition = function createDOMTreeForDefinition(definition) {

            return definition.map(function(def) {

                def = mergeObjects(definitionOutline, def);

                if (typeof def.root === 'string') {
                    def.root = document.createElement(def.root);
                } else {
                    def.root = document.createElement('span');
                }

                if (def.transform) {
                    def.root.dataset.transform = def.transform;
                }

                if (def.className) {
                    def.root.className = def.className;
                }

                if (def.overlay) {
                    def.root.dataset.overlay = def.overlay;
                }

                if (def.view) {
                    def.root.dataset.view = def.view;
                    if (def.style) {
                        def.root.dataset.style = def.style;
                    }
                    def.repeat = null;
                } else {

                    if (def.layout) {
                        def.root.dataset.layout = def.layout;
                    }

                    if (def.repeat) {
                        def.root.dataset.repeat = true;
                        def.repeat = createDOMTreeForDefinition(def.children).pop();
                    } else if (def.children) {
                        def.children = createDOMTreeForDefinition(def.children);
                        def.children.forEach(function(child) {
                            def.root.appendChild(child.root);
                        });
                    }
                }

                return def;
            });
        };

        /**
         * Presenting values
         */
        var createPresenterByDefinition = function createPresenterByDefinition(definition, presentDefinition) {

            var presenter = void 0;

            if (definition.repeat) {
                presenter = createPresenterRepeater(definition.root, definition.repeat, presentDefinition);
            } else if (typeof definition.view === 'string') {
                presenter = createPresenterView(definition.view, definition.root, definition.style);
            } else if (isRootDefinition(definition)) {
                presenter = createPresenterRoot(definition.root, definition.children, presentDefinition);
            }

            return presenter;
        };

        var presentTick = function presentTick(instance) {

            var isDrawing = false;

            var update = function update(definition, value) {

                definition.transform(value, function(output) {
                    definition.presenter.update(output);
                }, instance);

                if (!isDrawing) {
                    isDrawing = true;
                    draw();
                }
            };

            var draw = function draw() {
                instance.baseDefinition.presenter.draw();
                requestAnimationFrame(draw);
            };

            var presentDefinition = function presentDefinition(definition) {
                definition.presenter = createPresenterByDefinition(definition, presentDefinition);
                definition.transform = toTransformComposition(definition.transform, instance);
                return update;
            };

            return presentDefinition(instance.baseDefinition);
        };

        /**
         * Transform
         */
        var composeAsync = function composeAsync(instance) {
            for (var _len = arguments.length, funcs = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
                funcs[_key - 1] = arguments[_key];
            }

            return function(initialValue, callback) {

                function compose(i, value) {

                    // return end result
                    if (funcs.length <= i) {
                        callback(value);
                        return;
                    }

                    funcs[i](value, partial(compose, [i + 1]), instance);
                }

                compose(0, initialValue);
            };
        };

        var partial = function partial(fn) {
            var initialArgs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
            var ctx = arguments[2];

            return function() {
                var args = Array.from(initialArgs);
                Array.prototype.push.apply(args, arguments);
                return fn.apply(ctx, args);
            };
        };

        var toTransformComposition = function toTransformComposition(string, instance) {

            // no composition
            if (!string) {
                return function(value, cb) {
                    return cb(value);
                };
            }

            // already function no need to compose
            if (typeof string === 'function') {
                return string;
            }

            // wrap in default transform
            // if is single transform force parenthesis as it must be a fn
            var result = parseTransformChain('transform(' + (/^[a-z]+$/.test(string) ? string + '()' : string) + ')');
            return compose(result, instance);
        };

        var compose = function compose(chain, instance) {

            var composition = chain.map(function(item) {

                // get name
                var name = item.shift();

                // get related function
                var func = getExtension(ExtensionType.TRANSFORM, name) || function(value, cb, instance) {
                    cb(value);
                };

                // other items in array are parameters
                var params = item.map(function(parameter) {

                    // if is array turn into function
                    if (Array.isArray(parameter)) {

                        // normal transform
                        if (typeof parameter[0] === 'string') {
                            return compose([parameter], instance);
                        }

                        // chain of transforms
                        return compose(parameter, instance);
                    }

                    return toParameter(parameter);
                });

                return func.apply(undefined, toConsumableArray(params));
            });

            return composeAsync.apply(undefined, [instance].concat(toConsumableArray(composition)));
        };

        var toFunctionOutline = function toFunctionOutline(string) {
            var name = string.match(/[a-z]+/)[0];
            var parameters = toParameters(string.substring(name.length));
            return {
                name: name,
                parameters: parameters
            };
        };

        var toParameters = function toParameters(string) {
            return (string.match(/('.+?')|(".+?")|(\[.+?])|([.:\-\d\sa-zA-Z]+%?)/g) || []).map(trim).filter(function(str) {
                return str.length;
            }).map(toParameter);
        };

        var trimEdges = function trimEdges(string) {
            return string.substring(1, string.length - 1);
        };

        var isProbablyISODate = /^([\d]{4}-[\d]{1,2}-[\d]{1,2})/;
        var isBoolean = /^(true|false)$/;
        var isString = /^[\a-zA-Z]+$/;
        var isZeroString = /^0[\d]+/;
        var isQuotedString = /^('|")/;
        var isNumber = /^-?(?:\d+)?(?:\.|0\.)?[\d]+$/;
        var isArray = /^(\[)/;

        var toParameter = function toParameter(string) {

            if (isBoolean.test(string)) {
                return string === 'true';
            }

            if (isArray.test(string)) {
                return toParameters(trimEdges(string));
            }

            if (isProbablyISODate.test(string)) {
                return dateFromISO(string);
            }

            if (isQuotedString.test(string)) {
                return trimEdges(string);
            }

            if (isString.test(string) || isZeroString.test(string)) {
                return string;
            }

            if (isNumber.test(string)) {
                return parseFloat(string);
            }

            // is CSS unit (parsing will be handled by function that receives this value)
            return string;
        };

        var toCSSValue = function toCSSValue(value) {
            var parts = (value + '').match(/(-?[.\d]+)(%|ms|s|deg|cm|em|ch|ex|q|in|mm|pc|pt|px|vh|vw|vmin|vmax)?/);
            return {
                value: parseFloat(parts[1]),
                units: parts[2]
            };
        };

        /**
         * @param a { object }
         * @param b { object }
         */
        var mergeObjects = function mergeObjects(a) {
            var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};


            var key = void 0;
            var obj = {};

            for (key in a) {
                if (!a.hasOwnProperty(key)) {
                    continue;
                }
                obj[key] = typeof b[key] === 'undefined' ? a[key] : b[key];
            }

            return obj;
        };

        /**
         * @param string { string }
         */
        var toFunctionReference = function toFunctionReference(string) {
            var ref = window;
            var levels = string.split('.');
            levels.forEach(function(level, index) {
                if (!ref[levels[index]]) {
                    return;
                }
                ref = ref[levels[index]];
            });
            return ref !== window ? ref : null;
        };

        /**
         *
         */
        var toValue = function toValue(string) {

            // capture for object string
            if (/^(?:[\w]+\s?:\s?[\w.]+,\s?)+(?:[\w]+\s?:\s?[\w.]+)$/g.test(string)) {
                return string.match(/(?:(\w+)\s?:\s?([\w.]+))/g).reduce(function(obj, current) {
                    var parts = current.split(':');
                    obj[parts[0]] = toParameter(parts[1]);
                    return obj;
                }, {});
            }

            // handle the other options
            return toParameter(string);
        };

        /**
         * @param value { * }
         */


        /**
         * @param value { * }
         */
        var toInt = function toInt(value) {
            return parseInt(value, 10);
        };

        /**
         * @param string { string }
         */
        var trim = function trim(string) {
            return string.trim();
        };

        /**
         * @param string { string }
        export const lowercaseFirstLetter = (string) => string.charAt(0).toLowerCase() + string.slice(1);
         */

        /**
         * @param string { string }
         */
        var capitalizeFirstLetter = function capitalizeFirstLetter(string) {
            return string.charAt(0).toUpperCase() + string.slice(1);
        };

        /**
         * @param string { string }
         */
        var dashesToCamels = function dashesToCamels(string) {
            return string.replace(/-./g, function(sub) {
                return sub.charAt(1).toUpperCase();
            });
        };

        /**
         * @param string
        export const camelsToDashes = string => string.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
         */

        /**
         * @param obj { object }
         */
        var clone = function clone(obj) {
            if ((typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object' && obj !== null) {
                return JSON.parse(JSON.stringify(obj));
            }
            return obj;
        };

        var copyArray = function copyArray(arr) {
            return arr.slice();
        };

        var random = function random() {
            var min = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
            var max = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;

            return min + Math.random() * (max - min);
        };

        var range = function range(n) {
            var arr = [];
            var i = 0;
            for (; i < n; i++) {
                arr.push(i);
            }
            return arr;
        };

        var shuffle = function shuffle(a) {
            for (var i = a.length; i; i--) {
                var j = Math.floor(Math.random() * i);
                var _ref = [a[j], a[i - 1]];
                a[i - 1] = _ref[0];
                a[j] = _ref[1];
            }
        };



        var now = function now() {
            return window.performance.now();
        };

        var request = function request(url, success, error, options) {
            var xhr = new XMLHttpRequest();
            if (options) {
                options(xhr);
            }
            xhr.open('GET', url, true);
            xhr.onload = function() {
                success(xhr.response);
            };
            if (error) {
                xhr.onerror = function() {
                    error(xhr, xhr.status);
                };
            }
            xhr.send();
        };

        var equal = function equal(a, b) {
            if (isObject(a)) {
                return equalObjects(a, b);
            }
            if (Array.isArray(a)) {
                return equalArrays(a, b);
            }
            return a === b;
        };

        var isObject = function isObject(obj) {
            return obj === Object(obj);
        };

        var equalObjects = function equalObjects(a, b) {
            for (var i in a) {
                if (!b.hasOwnProperty(i) || a[i] !== b[i]) {
                    return false;
                }
            }
            return true;
        };

        var equalArrays = function equalArrays(a, b) {
            return a.length == b.length && a.every(function(v, i) {
                return v === b[i];
            });
        };

        var keysToList = function keysToList(obj) {
            return Object.keys(obj).map(function(k) {
                return '"' + k + '"';
            }).join(', ');
        };

        /**
         * Tick
         */

        var Tick = function() {
            function Tick() {
                var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
                var element = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document.createElement('div');
                classCallCheck(this, Tick);


                // set base configuration options
                this._options = mergeObjects(Tick.options(), options);

                // instance properties
                this._element = element;
                this._value = null;
                this._observer = null;
                this._viewDefinition = null;
                this._constants = null;
                this._presets = null;
                this._updater = null;
                this._credits = null;

                // callback methods
                this._didInit = null;
                this._didDestroy = null;
                this._willDestroy = null;
                this._didUpdate = null;

                // initialise Tick
                this._init();
            }

            /**
             * Default options for this control
             */


            createClass(Tick, [{
                key: 'isRootElement',


                /**
                 * Public API
                 */
                value: function isRootElement(element) {
                    return this._element === element;
                }
            }, {
                key: 'setConstant',
                value: function setConstant$$1(key, value) {
                    this._constants[key] = value;
                }
            }, {
                key: 'getConstants',
                value: function getConstants$$1() {
                    return this._constants;
                }
            }, {
                key: 'getConstant',
                value: function getConstant(key) {
                    return this._constants[key];
                }
            }, {
                key: 'setPreset',
                value: function setPreset$$1(key, fn) {
                    this._presets[key] = fn;
                }
            }, {
                key: 'getPreset',
                value: function getPreset(key) {
                    return this._presets[key];
                }
            }, {
                key: 'destroy',
                value: function destroy$$1() {
                    this._willDestroy(this);

                    // clean up
                    this._observer.disconnect();

                    // destroy presenters
                    this.baseDefinition.presenter.destroy();

                    this._didDestroy(this);
                }
            }, {
                key: 'redraw',
                value: function redraw() {
                    if (!this.baseDefinition || !this.baseDefinition.presenter) return;
                    this.baseDefinition.presenter.reset();
                    this.baseDefinition.presenter.draw();
                    this._updater(this.baseDefinition, this._value);
                }

                /**
                 * Private Methods
                 */

            }, {
                key: '_init',
                value: function _init() {
                    var _this = this;

                    // move options to properties
                    this._viewDefinition = this._options.view;
                    this._willDestroy = this._options.willDestroy;
                    this._didDestroy = this._options.didDestroy;
                    this._didInit = this._options.didInit;
                    this._didUpdate = this._options.didUpdate;
                    this._value = this._options.value;
                    this._presets = this._options.presets;
                    this._constants = this._options.constants;
                    this._credits = this._options.credits;

                    // no more use of options behind this line
                    // ---------------------------------------

                    // always add class tick to element (make sure it's only added once)
                    if (!this._element.classList.contains('tick')) {
                        this._element.classList.add('tick');
                    }

                    // use mutation observer to detect changes to value attribute
                    this._observer = observeAttributes(this._element, ['data-value'], function(value) {
                        _this.value = value;
                    });

                    // force default view root, move children of current root to this element
                    if (this._viewDefinition.root !== this._element) {
                        Array.from(this._viewDefinition.root.children).forEach(function(node) {
                            _this._element.appendChild(node);
                        });
                        this._viewDefinition.root = this._element;
                    }

                    // no default view presenter defined, fallback to text
                    if (!this._viewDefinition.view && !this._viewDefinition.children) {
                        this._viewDefinition.view = 'text';
                    }

                    // setup root presenter
                    this._updater = presentTick(this);

                    // update for first time
                    if (this.value !== null) {
                        this._update(this.value);
                    }

                    // set to ready state
                    this._element.dataset.state = 'initialised';

                    // done with init
                    this._didInit(this, this.value);

                    // credits
                    if (this._credits) {
                        var credits = document.createElement('a');
                        credits.className = 'tick-credits';
                        credits.href = this._credits.url;
                        credits.tabindex = -1;
                        credits.target = '_blank';
                        credits.rel = 'noopener noreferrer';
                        credits.textContent = this._credits.label;
                        this._element.appendChild(credits);
                    }
                }
            }, {
                key: '_update',
                value: function _update(value) {

                    this._updater(this.baseDefinition, value);

                    this._didUpdate(this, value);
                }
            }, {
                key: 'baseDefinition',


                /**
                 * Public Properties
                 */
                get: function get$$1() {
                    return this._viewDefinition;
                }
            }, {
                key: 'root',
                get: function get$$1() {
                    return this._element;
                }
            }, {
                key: 'value',
                get: function get$$1() {
                    return this._value;
                },
                set: function set$$1(value) {
                    this._value = typeof value === 'string' ? toValue(value) : value;
                    this._update(value);
                }
            }], [{
                key: 'options',
                value: function options() {
                    return {
                        constants: getConstants(),
                        presets: getPresets(),
                        value: null,
                        view: null,
                        didInit: function didInit(tick) {},
                        didUpdate: function didUpdate(tick, value) {},
                        willDestroy: function willDestroy(tick) {},
                        didDestroy: function didDestroy(tick) {},
                        credits: {
                            label: 'Powered by 胡高团',
                            url: ''
                        }
                    };
                }
            }]);
            return Tick;
        }();

        var transformDurationUnit = function transformDurationUnit(value, single, plural, progress) {
            return {
                label: value === 1 ? single : plural,
                progress: value / progress,
                value: value
            };
        };

        /**
         * Tick DOM interface
         */
        var instances = [];

        var setConstant = function setConstant(key, value) {
            constants[key] = value;
        };

        var setPreset = function setPreset(key, value) {
            presets[key] = value;
        };

        var getConstants = function getConstants() {
            return constants;
        };

        var getPresets = function getPresets() {
            return presets;
        };

        var constants = {
            YEAR_PLURAL: 'Years',
            YEAR_SINGULAR: 'Year',
            MONTH_PLURAL: 'Months',
            MONTH_SINGULAR: 'Month',
            WEEK_PLURAL: 'Weeks',
            WEEK_SINGULAR: 'Week',
            DAY_PLURAL: 'Days',
            DAY_SINGULAR: 'Day',
            HOUR_PLURAL: 'Hours',
            HOUR_SINGULAR: 'Hour',
            MINUTE_PLURAL: 'Minutes',
            MINUTE_SINGULAR: 'Minute',
            SECOND_PLURAL: 'Seconds',
            SECOND_SINGULAR: 'Second',
            MILLISECOND_PLURAL: 'Milliseconds',
            MILLISECOND_SINGULAR: 'Millisecond'
        };

        var presets = {
            y: function y(value, constants) {
                return transformDurationUnit(value, constants.YEAR_SINGULAR, constants.YEAR_PLURAL, 10);
            },
            M: function M(value, constants) {
                return transformDurationUnit(value, constants.MONTH_SINGULAR, constants.MONTH_PLURAL, 12);
            },
            w: function w(value, constants) {
                return transformDurationUnit(value, constants.WEEK_SINGULAR, constants.WEEK_PLURAL, 52);
            },
            d: function d(value, constants) {
                return transformDurationUnit(value, constants.DAY_SINGULAR, constants.DAY_PLURAL, 365);
            },
            h: function h(value, constants) {
                return transformDurationUnit(value, constants.HOUR_SINGULAR, constants.HOUR_PLURAL, 24);
            },
            m: function m(value, constants) {
                return transformDurationUnit(value, constants.MINUTE_SINGULAR, constants.MINUTE_PLURAL, 60);
            },
            s: function s(value, constants) {
                return transformDurationUnit(value, constants.SECOND_SINGULAR, constants.SECOND_PLURAL, 60);
            },
            mi: function mi(value, constants) {
                return transformDurationUnit(value, constants.MILLISECOND_SINGULAR, constants.MILLISECOND_PLURAL, 1000);
            }
        };

        var attributes = {
            'value': toValue,
            'didInit': toFunctionReference,
            'didUpdate': toFunctionReference,
            'didDestroy': toFunctionReference,
            'willDestroy': toFunctionReference
        };

        var getOptionsFromAttributes = function getOptionsFromAttributes(element) {
            var transfomers = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
            var defaults$$1 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};


            var dataset = element.dataset;

            var options = {
                meta: {}
            };

            for (var prop in dataset) {

                if (!dataset.hasOwnProperty(prop)) {
                    continue;
                }

                var valueTransformer = transfomers[prop];
                var value = dataset[prop];

                if (valueTransformer) {
                    value = valueTransformer(value);
                    value = value === null ? clone(defaults$$1[prop]) : value;
                    options[prop] = value;
                }
            }

            if (dataset.credits === 'false') {
                options.credits = false;
            }

            return options;
        };

        var indexOfElement = function indexOfElement(instances, element) {
            var i = 0;
            var l = instances.length;
            for (; i < l; i++) {
                if (instances[i].isRootElement(element)) {
                    return i;
                }
            }
            return -1;
        };

        var parse = function parse(context) {

            var elements = void 0;
            var element = void 0;
            var i = void 0;
            var instances = [];

            // find all crop elements and bind Crop behavior
            elements = context.querySelectorAll('.tick:not([data-state])');
            i = elements.length;

            while (i--) {
                element = elements[i];
                instances.push(create(element));
            }

            return instances;
        };

        var find = function find(element) {
            var result = instances.filter(function(instance) {
                return instance.isRootElement(element);
            });
            return result ? result[0] : null;
        };

        var getDefaultOptions = function getDefaultOptions() {
            return _extends({}, Tick.options(), {
                constants: _extends({}, constants),
                presets: _extends({}, presets)
            });
        };

        var create = function create() {
            var element = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
            var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;


            // if first argument is options object correct parameter values
            if (element && !isHTMLElement(element)) {
                options = element;
                element = undefined;
            }

            // if already in array, can't create another on this location in the DOM
            if (element && find(element)) {
                return;
            }

            // if view defined
            if (options && options.view) {
                options.view = createDOMTreeForDefinition([options.view])[0];
            }

            // if no options supplied, get the options from the element attributes
            if (!options && element) {
                options = getOptionsFromAttributes(element, attributes, getDefaultOptions());
            }

            // if element supplied, view is either default view or defined by child elements
            if (element) {

                // no options defined, set blank options object
                if (!options) {
                    options = {};
                }

                // no default view defined
                if (!options.view) {
                    options.view = toPresenterDefinitionTree([element])[0];
                }
            }

            // instance (pass element to set root)
            var instance = new Tick(options, element);

            // add new instance
            instances.push(instance);

            // return the instance
            return instance;
        };

        var destroy = function destroy(element) {

            var index = indexOfElement(instances, element);

            if (index < 0) {
                return false;
            }

            instances[index].destroy();
            instances.splice(index, 1);

            return true;
        };

        var time = function time(fn) {
            return function() {
                setTimeout(fn, 0);
            };
        };

        var now$2 = function now$$1() {
            return Date.now();
        };

        var setTimer = function setTimer(cb) {
            var interval = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1000;
            var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};


            var settings = mergeObjects({
                autostart: true
            }, options);

            var tickExpectedTime = null;
            var tickStartTime = null;
            var sleepStartTime = null;
            var totalSleepTime = 0;
            var sleepIntervalOffset = null;

            var paused = false;

            var timer = null;

            var isPaused = function isPaused() {
                return paused;
            };

            var isStarted = function isStarted() {
                return tickStartTime !== null;
            };

            var isDocumentHidden = function isDocumentHidden() {
                return document.hidden;
            };

            // timer tick
            var tick = function tick() {

                var currentTime = now$2();

                var timeoutErrorOffset = tickExpectedTime - currentTime;

                var timeout = interval + timeoutErrorOffset;

                // calculate new expected time
                tickExpectedTime = currentTime + timeout;

                // calculate total runtime
                var runtime = currentTime - tickStartTime - totalSleepTime + timeoutErrorOffset;

                // let others know total runtime of counter
                cb(runtime);

                // new timeout
                timer = setTimeout(tick, timeout);
            };

            var start = function start() {

                // if paused, run resume instead (makes building a stopwatch easier)
                if (isPaused()) {
                    resume();
                    return;
                }

                // if already running we don't do anything, can't start twice need to stop first
                if (isStarted()) {
                    return;
                }

                // the moment we set the timeout
                tickStartTime = now$2();

                // call callback immidiately with zero value
                setTimeout(function() {
                    cb(0);
                }, 0);

                // listen for changes in visibility
                startListeningForVisibilityChanges();

                // stop here if document is hidden at start time
                if (isDocumentHidden()) {
                    didHideDocument();
                    return;
                }

                // the moment the timeout should end
                tickExpectedTime = now$2() + interval;

                // start ticking
                timer = setTimeout(function() {
                    tick();
                }, interval);
            };

            var stop = function stop() {
                // can always stop

                clearTimeout(timer);
                timer = null;
                tickStartTime = null;
                tickExpectedTime = null;
                sleepStartTime = null;
                totalSleepTime = 0;
                sleepIntervalOffset = null;
                paused = false;

                stopListeningForVisibilityChanges();
            };

            var reset = function reset() {
                // can always reset
                stop();
                start();
            };

            /**
             * Pause / Resume
             */
            var pause = function pause() {

                // can't pause if not running or if is hidden
                if (!isStarted() || isDocumentHidden()) {
                    return;
                }

                paused = true;

                stopListeningForVisibilityChanges();

                sleep();
            };

            var resume = function resume() {

                // can't resume if not paused if not started or if hidden
                if (!isPaused() || !isStarted() || isDocumentHidden()) {
                    return;
                }

                paused = false;

                startListeningForVisibilityChanges();

                wake();
            };

            // start sleeping
            var sleep = function sleep() {
                clearTimeout(timer);
                sleepStartTime = now$2();
                sleepIntervalOffset = tickExpectedTime - sleepStartTime;
            };

            // wake from hidden or pause stated
            var wake = function wake() {

                // need to remember the wait duration
                totalSleepTime += now$2() - sleepStartTime;
                sleepStartTime = null;

                // as we are going to call tick immidiately we expect the time to be now
                tickExpectedTime = now$2() + sleepIntervalOffset;

                // start ticking
                timer = setTimeout(function() {
                    tick();
                }, sleepIntervalOffset);
            };

            /**
             * Document Visibility Change
             */
            var didHideDocument = function didHideDocument() {

                // can only be called if the timer is currently running so no checks
                sleep();
            };

            var didShowDocument = function didShowDocument() {

                // can only be called if the timer was running before (but could have been stopped in the mean time)
                if (!isStarted()) {
                    return;
                }

                wake();
            };

            var stopListeningForVisibilityChanges = function stopListeningForVisibilityChanges() {
                document.removeEventListener('visibilitychange', handleVisibilityChange);
            };

            var startListeningForVisibilityChanges = function startListeningForVisibilityChanges() {
                document.addEventListener('visibilitychange', handleVisibilityChange);
            };

            var handleVisibilityChange = function handleVisibilityChange() {
                if (isDocumentHidden()) {
                    didHideDocument();
                } else {
                    didShowDocument();
                }
            };

            /**
             * Go time! (or not)
             */
            if (settings.autostart) {
                start();
            }

            /**
             * API
             */
            return {
                start: start,
                stop: time(stop),
                reset: time(reset),
                pause: time(pause),
                resume: resume
            };
        };

        var toInterval = function toInterval(string) {
            if (!/^[\d]+/.test(string)) {
                string = '1 ' + string;
            }
            var parts = string.split(' ');
            return parseFloat(parts[0]) * TimeUnit[parts[1].toLowerCase()];
        };

        var toTime = function toTime(date, time) {
            return setTime(date, time.split(':').map(toInt));
        };

        var toYearlyMoment = function toYearlyMoment(date, string) {

            /*
  every 1st of november at 12:00
  every 25th of november at 13:00 wait 10 seconds
  every 25th of november from 10 till 15 every 10 minutes
  */

            var parts = string.match(/januari|februari|march|april|may|june|july|august|september|october|november|december|[\d]+th|\dst|\dnd|first|last|at\s[\d]+(?::[\d]+)?(?::[\d]+)?/g);

            // no `at time` supplied
            if (parts.length > 1) {
                var rest = '';
                parts.forEach(function(p) {
                    rest = string.split(p)[1] || '';
                });
                var wait = rest.trim().match(/wait\s[\d]+\s[a-z]+/);
                if (wait) {
                    parts.push(wait[0]);
                }
            }

            // to moment object
            var moment = parts.reduce(function(obj, part) {

                // is month day (1st, 2nd, 12th, first, last)
                if (/([\d]+th|\dst|\dnd|first|last)/.test(part)) {
                    obj.day = /^[\d]/.test(part) ? parseInt(part, 10) : part === 'first' ? 1 : part;
                }

                // if is time (at 12:00)
                if (/^at/.test(part)) {
                    obj.time = toTime(clone$1(date), part.substr(3));
                }

                // is waiting period
                else if (/wait/.test(part)) {
                    obj.idle = toInterval(part.substr(5));
                }

                // must be month
                else if (/^[\a-zA-Z]+$/.test(part)) {
                    obj.month = part;
                }

                return obj;
            }, {
                idle: null,
                day: null,
                month: null,
                time: null,
                date: null,
                dist: null,
                wait: false
            });

            if (!moment.time) {

                // set to day
                // move to first day (so when the month changes its not accidentally out of range)
                moment.time = clone$1(date);
                moment.time.setDate(1);
                moment.time = setMonth(moment.time, moment.month);
                moment.time = setDayOfMonth(moment.time, moment.day);

                // if so get first valid date and use that for time
                var hourlyMoment = toHourlyMoment(moment.time, string);

                // waiting
                if (hourlyMoment.wait) {
                    return moment;
                }

                // copy either date or from time
                moment.time = clone$1(sameDate(date, moment.time) && hourlyMoment.date ? hourlyMoment.date : hourlyMoment.from);

                // test if has already passed, if so, set to hourly from for next month
                var dist = moment.time - date;
                if (dist < 0) {

                    // move to next year
                    moment.time = clone$1(hourlyMoment.from);
                    moment.time.setFullYear(moment.time.getFullYear() + 1);

                    // recalculate distance
                    dist = moment.time - date;
                }

                moment.dist = dist;
            } else {

                // correct time to given month
                moment.time.setDate(1);
                moment.time = setMonth(moment.time, moment.month);
                moment.time = setDayOfMonth(moment.time, moment.day);

                var _dist = moment.time - date;
                var distOverflow = 0;
                if (_dist < 0) {

                    distOverflow = _dist;

                    // move to next year
                    moment.time.setFullYear(moment.time.getFullYear() + 1);

                    // recalculate distance
                    _dist = moment.time - date;
                }

                // get total time from today to next moment
                if (moment.idle !== null && distOverflow + moment.idle > 0) {
                    moment.wait = true;
                    return moment;
                }

                moment.dist = _dist;
            }

            moment.date = clone$1(moment.time);

            return moment;
        };

        var toMonthlyMoment = function toMonthlyMoment(date, string) {

            /*
  every month on the 1st day
  every month on the first day
  every month on day the 12th
  every last day of the month at 12:00
  every first day of the month
  every 1st day of the month at 10
  every 2nd day of the month at 10
  every 20th day of the month
  every 20th day of the month at 12:00 wait 10 minutes
  every 20th day of the month from 10 till 14 every hour
  */

            var parts = string.match(/[\d]+th|\dst|\dnd|first|last|at\s[\d]+(?::[\d]+)?(?::[\d]+)?/g);

            // no `at time` supplied
            if (parts.length > 1) {
                var rest = '';
                parts.forEach(function(p) {
                    rest = string.split(p)[1] || '';
                });
                var wait = rest.trim().match(/wait\s[\d]+\s[a-z]+/);
                if (wait) {
                    parts.push(wait[0]);
                }
            }

            var moment = parts.reduce(function(obj, part) {

                // is month day (1st, 2nd, 12th, first, last)
                if (/([\d]+th|\dst|\dnd|first|last)/.test(part)) {
                    obj.day = /^[\d]/.test(part) ? parseInt(part, 10) : part === 'first' ? 1 : part;
                }

                // if is time (at 12:00)
                if (/^at/.test(part)) {
                    obj.time = toTime(clone$1(date), part.substr(3));
                }

                // is waiting period
                else if (/wait/.test(part)) {
                    obj.idle = toInterval(part.substr(5));
                }

                return obj;
            }, {
                idle: null,
                day: null,
                time: null,
                date: null,
                dist: null,
                wait: false
            });

            if (!moment.time) {

                // set to day
                moment.time = setDayOfMonth(clone$1(date), moment.day);

                // if so get first valid date and use that for time
                var hourlyMoment = toHourlyMoment(moment.time, string);

                // waiting
                if (hourlyMoment.wait) {
                    return moment;
                }

                // copy either date or from time
                moment.time = clone$1(sameDate(date, moment.time) && hourlyMoment.date ? hourlyMoment.date : hourlyMoment.from);

                // test if has already passed, if so, set to hourly from for next month
                var dist = moment.time - date;
                if (dist < 0) {

                    // move to next month (set to first day of month)
                    moment.time = clone$1(hourlyMoment.from);
                    moment.time.setDate(1);
                    moment.time.setMonth(moment.time.getMonth() + 1);

                    // now set to expected day
                    setDayOfMonth(moment.time, moment.day);

                    // recalculate distance
                    dist = moment.time - date;
                }

                moment.dist = dist;
            } else {

                // correct time to set week day
                moment.time = setDayOfMonth(moment.time, moment.day);

                var _dist2 = moment.time - date;
                var distOverflow = 0;
                if (_dist2 < 0) {

                    distOverflow = _dist2;

                    // move to next month (set to first day of month)
                    moment.time.setDate(1);
                    moment.time.setMonth(moment.time.getMonth() + 1);

                    // now set to expected day
                    setDayOfMonth(moment.time, moment.day);

                    // recalculate distance
                    _dist2 = moment.time - date;
                }

                // get total time from today to next moment
                if (moment.idle !== null && distOverflow + moment.idle > 0) {
                    moment.wait = true;
                    return moment;
                }

                moment.dist = _dist2;
            }

            moment.date = clone$1(moment.time);

            return moment;
        };

        var toWeeklyMoment = function toWeeklyMoment(date, string) {

            // - every wednesday at 12:00
            // - every wednesday at 12:00 wait 10 minutes
            // - wednesday every hour
            // - wednesday from 10 till 14 every hour
            // - wednesday 12:00, thursday 14:00
            // - tuesday 10:00 wait 2 hours
            // - tuesday 10:00 wait 2 hours, saturday 10:00 wait 2 hours
            // - every tuesday every 5 minutes
            // - wednesday from 10 till 14 every hour
            // - every tuesday every 5 minutes wait 10 seconds
            // - every tuesday from 10 till 12 every 5 minutes wait 10 seconds
            // - every tuesday every 5 minutes from 10 till 12 wait 10 seconds
            // - every tuesday at 12:00 wait 5 minutes

            // strip week part and then feed rest to toDaily() or Hourly() method
            var parts = string.match(/(?:mon|tues|wednes|thurs|fri|satur|sun)day|at\s[\d]+(?::[\d]+)?(?::[\d]+)?/g);

            // no `at time` supplied
            if (parts.length > 1) {
                var rest = '';
                parts.forEach(function(p) {
                    rest = string.split(p)[1] || '';
                });
                var wait = rest.trim().match(/wait\s[\d]+\s[a-z]+/);
                if (wait) {
                    parts.push(wait[0]);
                }
            }

            // to moment object
            var moment = parts.reduce(function(obj, part) {

                // is day
                if (/(?:mon|tues|wednes|thurs|fri|satur|sun)day/.test(part)) {
                    obj.day = Days[capitalizeFirstLetter(part)];
                }

                // if is time (at 12:00)
                if (/^at/.test(part)) {
                    obj.time = toTime(clone$1(date), part.substr(3));
                }

                // is waiting period
                else if (/wait/.test(part)) {
                    obj.idle = toInterval(part.substr(5));
                }

                return obj;
            }, {
                idle: null,
                day: null,
                time: null,
                date: null,
                dist: null,
                wait: false
            });

            // if no time set see if a hourly period was defined
            if (!moment.time) {

                // set to day
                moment.time = setDay(clone$1(date), moment.day);

                // if so get first valid date and use that for time
                var hourlyMoment = toHourlyMoment(moment.time, string);

                // waiting
                if (hourlyMoment.wait) {
                    return moment;
                }

                // copy either date or from time
                moment.time = clone$1(sameDate(date, moment.time) && hourlyMoment.date ? hourlyMoment.date : hourlyMoment.from);

                // test if has already passed, if so, set to hourly from for next week
                var dist = moment.time - date;

                if (dist < 0) {
                    moment.time.setDate(moment.time.getDate() + 7);
                }

                moment.dist = dist;
            } else {

                // correct time to set week day
                moment.time = setDay(moment.time, moment.day);

                var _dist3 = moment.time - date;
                if (_dist3 < 0) {
                    moment.time.setDate(moment.time.getDate() + 7);
                    _dist3 = moment.time - date;
                }

                // if is idling
                if (moment.idle !== null && _dist3 >= TimeUnit.Week - moment.idle) {
                    moment.wait = true;
                    return moment;
                }

                moment.dist = _dist3;
            }

            moment.date = clone$1(moment.time);

            return moment;
        };

        var toDailyMoment = function toDailyMoment(date, string) {
            // - every day at 10
            // - every day at 14:00
            // - every day at 14:30 wait 5 minutes

            // get parts
            var parts = string.match(/([\d]+(?::[\d]+)?(?::[\d]+)?)|(wait\s[\d]+\s[a-z]+)/g);

            // to moment object
            var moment = parts.reduce(function(obj, part) {

                // if is time
                if (/^[\d]/.test(part)) {
                    obj.time = toTime(clone$1(date), part);
                }

                // is waiting period
                else if (/wait/.test(part)) {
                    obj.idle = toInterval(part.substr(5));
                }

                return obj;
            }, {
                idle: null,
                time: null,
                date: null,
                wait: false,
                dist: null
            });

            var dist = moment.time - date;

            // if time dist is negative set time to tomorrow
            if (dist < 0) {
                moment.time.setDate(moment.time.getDate() + 1);
                dist = moment.time - date;
            }

            // test if wait period has passed
            if (moment.idle !== null && dist >= TimeUnit.Day - moment.idle) {
                moment.wait = true;
                return moment;
            }

            moment.dist = dist;
            moment.date = clone$1(moment.time);

            return moment;
        };

        var toHourlyMoment = function toHourlyMoment(date, string) {

            // - from 10 till 20 every hour wait 5 minutes
            // - from 10:00:00 till 14:00 every 15 minutes
            // - every hour
            // - every 20 minutes
            // - every 30 seconds

            // get parts
            var parts = string.match(/((?:[\d]+\s)?(?:hours|hour|minutes|minute|seconds|second))|((?:from|till)\s[\d]+(?::[\d]+)?(?::[\d]+)?)|(wait\s[\d]+\s[a-z]+)/g);

            // to moment object
            var moment = parts.reduce(function(obj, part) {

                // if is time
                if (/from/.test(part)) {
                    obj.from = toTime(obj.from, part.split(' ')[1]);
                } else if (/till/.test(part)) {
                    obj.till = toTime(obj.till, part.split(' ')[1]);
                }

                // is waiting period
                else if (/wait/.test(part)) {
                    obj.idle = toInterval(part.substr(5));
                }

                // if is interval
                else if (/hours|hour|minutes|minute|seconds|second/.test(part)) {
                    obj.interval = toInterval(part);
                }

                return obj;
            }, {
                idle: null,
                interval: null,
                date: null,
                dist: null,
                wait: false,
                from: toTime(clone$1(date), '0'),
                till: toTime(clone$1(date), '23:59:59:999')
            });

            // if valid moment
            if (date < moment.from || date >= moment.till) {
                return moment;
            }

            // calculate if interval fits in duration
            if (moment.interval > moment.till - moment.from) {
                return moment;
            }

            // time passed since start of moment
            var diff = date - moment.from;

            // interval duration minus all intervals that fitted in the passed time since start
            // 200 - (diff % interval)
            // 200 - (1450 % 200)
            // 200 - 50
            // 150 till next moment
            var dist = moment.interval - diff % moment.interval;

            // test if wait period has passed
            if (moment.idle !== null && dist >= moment.interval - moment.idle) {
                moment.wait = true;
                return moment;
            }

            // set as final distance
            moment.dist = dist;

            // turn into date by adding to current time
            moment.date = new Date(date.getTime() + moment.dist);

            return moment;
        };

        var toMoment = function toMoment(date, string) {

            // test yearly schedules
            if (/januari|februari|march|april|may|june|july|august|september|october|november|december/.test(string)) {
                return toYearlyMoment(date, string);
            }

            // test for monthly schedules
            if (/month/.test(string)) {
                return toMonthlyMoment(date, string);
            }

            // test for weekly schedules
            if (/(?:mon|tues|wednes|thurs|fri|satur|sun)day/.test(string)) {
                return toWeeklyMoment(date, string);
            }

            // test for daily schedules
            if (/day at/.test(string) || /^at /.test(string)) {
                return toDailyMoment(date, string);
            }

            // test for hourly schedules
            if (/hours|hour|minutes|minute|seconds|second/.test(string)) {
                return toHourlyMoment(date, string);
            }

            return null;
        };

        var getNextScheduledDate = function getNextScheduledDate(date, schedule) {

            // create moments
            var moments = schedule.split(',').map(trim) // remove whitespace
                .map(function(s) {
                    return toMoment(date, s);
                }); // string to moment in time

            // calculate closest moment
            var nearest = null;

            for (var i = 0; i < moments.length; i++) {

                var moment = moments[i];

                // currently waiting
                if (nearest === null && moment.wait) {
                    return null;
                }

                if (nearest === null) {
                    nearest = moment;
                } else if (nearest.dist === null && moment.dist !== null) {
                    nearest = moment;
                } else if (moment.dist !== null && moment.dist < nearest.dist) {
                    nearest = moment;
                }
            }

            // return nearest date
            return nearest.date;
        };

        /**
         * Returns the server offset based on the type of value
         * will subtract current time from server time resulting in a correction offset
         * @param server
         * @param cb
         * @returns {*}
         */
        var getServerOffset = function getServerOffset(server, cb) {
            if (server === true) {
                serverDate(function(date) {
                    cb(date.getTime() - now$1().getTime());
                });
                return;
            }
            if (typeof server === 'string') {
                setTimeout(function() {
                    cb(dateFromISO(server).getTime() - now$1().getTime());
                }, 0);
                return;
            }
            setTimeout(function() {
                cb(0);
            }, 0);
        };

        /**
         * Default options for counter
         * @type object
         */
        var DEFAULT_COUNTDOWN_OPTIONS = {
            format: ['d', 'h', 'm', 's'],
            cascade: true,
            server: null, // null (use client date) | true (use info in Date header) | ISO 8601 (fixed date)
            interval: 1000
        };

        /**
         * Creates a counter object
         * @param props
         */
        var createCounter = function createCounter(props) {
            return _extends({

                // read only
                complete: false,
                offset: null,
                value: null,
                timer: null,

                // api
                onload: function onload() {},
                onupdate: function onupdate(value) {}

            }, props);
        };

        /**
         * Countdown Value
         * @param total - total milliseconds to count down
         * @param options
         */
        var countdownAmount = function countdownAmount(total) {
            var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};


            if (typeof total !== 'number') {
                throw 'Can\'t start counter, the "milliseconds" parameter is required';
            }

            // set base options
            options = mergeObjects({
                units: 'seconds',
                target: 0,
                amount: 1000,
                interval: 1000
            }, options);

            // set private target
            var target = options.target;
            var current = total;

            // counter api
            var counter = createCounter({
                target: target,
                onended: function onended() {}
            });

            setTimeout(function() {

                // count method
                var count = function count(runtime) {

                    current = total - runtime / options.interval * options.amount;

                    // test if reached target date
                    if (current <= target) {

                        // set final value
                        counter.value = options.target;

                        // set output to zero
                        counter.onupdate(counter.value / TimeUnit[options.units]);

                        // stop timer
                        counter.timer.stop();

                        // we're done!
                        counter.onended();

                        // really done!
                        return;
                    }

                    // set value
                    counter.value = current;

                    // calculate duration
                    counter.onupdate(counter.value / TimeUnit[options.units]);
                };

                // set our counter, don't start automatically as we want to call onload first
                counter.timer = setTimer(count, options.interval, {
                    autostart: false
                });

                // ready!
                counter.complete = true;
                counter.onload();

                // start timer automatically
                counter.timer.start();
            }, 0);

            return counter;
        };

        /**
         * Count down towards date
         * @param due
         * @param options
         * @returns object
         */
        var countdownDuration = function countdownDuration(due) {
            var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};


            if (typeof due === 'undefined') {
                throw 'Can\'t start counter, the "due" parameter is required';
            }

            // set base options
            options = mergeObjects(DEFAULT_COUNTDOWN_OPTIONS, options);

            // set private target
            var target = isDate(due) ? due : dateFromISO(due);

            // counter api
            var counter = createCounter({
                due: clone$1(target),
                onended: function onended() {}
            });

            // get offset
            getServerOffset(options.server, function(offset) {

                counter.offset = offset;

                var count = function count() {

                    var now$$1 = offsetDate(offset);

                    // test if reached target date
                    if (target - now$$1 <= 0) {

                        // set final value
                        counter.value = new Array(options.format.length).fill(0);

                        // set output to zero
                        counter.onupdate(counter.value);

                        // stop timer
                        counter.timer.stop();

                        // we're done!
                        counter.onended();

                        // really done!
                        return;
                    }

                    // set value
                    counter.value = dateDiff(now$$1, target, options.format, options.cascade);

                    // calculate duration
                    counter.onupdate(counter.value);
                };

                // start our counter
                counter.timer = setTimer(count, options.interval, {
                    autostart: false
                });

                // ready!
                counter.complete = true;
                counter.onload();

                // run timer
                counter.timer.start();
            });

            return counter;
        };

        /**
         * Count up from date
         * @param since
         * @param options
         * @returns object
         */
        var countUpDuration = function countUpDuration(since) {
            var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};


            if (typeof since === 'undefined') {
                throw 'Can\'t start counter, the "since" parameter is required';
            }

            // set base options
            options = mergeObjects(DEFAULT_COUNTDOWN_OPTIONS, options);

            // set from date
            var from = isDate(since) ? since : dateFromISO(since);

            // counter api
            var counter = createCounter({
                since: clone$1(from)
            });

            // get offset
            getServerOffset(options.server, function(offset) {

                counter.offset = offset;

                var count = function count() {

                    var now$$1 = offsetDate(offset);

                    // set value
                    counter.value = dateDiff(from, now$$1, options.format, options.cascade);

                    // calculate duration
                    counter.onupdate(counter.value);
                };

                // start our counter
                counter.timer = setTimer(count, options.interval, {
                    autostart: false
                });

                // ready!
                counter.complete = true;
                counter.onload();

                // run timer
                counter.timer.start();
            });

            return counter;
        };

        /**
         * Count using a predefined schedule
         * @param schedule
         * @param options
         * @returns object
         */
        var countScheduled = function countScheduled(schedule) {
            var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};


            if (typeof schedule !== 'string') {
                throw 'Can\'t start scheduler, "schedule" is a required parameter';
            }

            // timezone is in ISO8601 timezone format
            options = mergeObjects(_extends({}, DEFAULT_COUNTDOWN_OPTIONS, {
                timezone: null
            }), options);

            // get timezone offset
            var timezone = options.timezone ? toTimezoneOffset(options.timezone) : null;

            // counter api
            var counter = createCounter({
                waiting: null,
                nextScheduledDate: null,
                previouslyScheduledDate: null,
                onrepeat: function onrepeat(nextDate, lastDate) {},
                onresume: function onresume(nextDate) {},
                onwait: function onwait(sinceDate) {}
            });

            // date scheduled during last check
            var lastDate = undefined;
            var nextDate = null;

            // get offset
            getServerOffset(options.server, function(offset) {

                counter.offset = offset;

                var count = function count() {

                    var now$$1 = offsetDate(offset);

                    if (timezone !== null) {
                        now$$1 = timezoneDate(now$$1, timezone);
                    }

                    // get next date
                    nextDate = getNextScheduledDate(now$$1, schedule);

                    // if no next date, we are in waiting state
                    counter.waiting = nextDate === null;

                    // if target is null call `wait` method
                    if (counter.waiting) {

                        // if is waiting initially
                        if (lastDate === undefined) {
                            lastDate = null;
                        }

                        // set output to zero
                        counter.value = new Array(options.format.length).fill(0);

                        // remember scheduled date if set
                        if (counter.nextScheduledDate) {
                            counter.previouslyScheduledDate = clone$1(counter.nextScheduledDate);
                        }

                        // update counter dates
                        counter.nextScheduledDate = nextDate === null ? null : clone$1(nextDate);

                        // update counter
                        counter.onwait(counter.previouslyScheduledDate ? clone$1(counter.previouslyScheduledDate) : null);

                        // we'll stop here but we'll leave the counter running
                        return;
                    }

                    // update counter dates
                    counter.nextScheduledDate = clone$1(nextDate);

                    // just now we did not have a date (last date is always the date from the previous loop),
                    // but now have, so we just woke up
                    if (lastDate === null) {
                        counter.onresume(clone$1(nextDate));
                    }

                    // if no last date or it's not the first loop and its not the same as the next date we are looping
                    if (lastDate === null || lastDate !== undefined && !sameTime(lastDate, nextDate)) {

                        counter.onrepeat(clone$1(nextDate), lastDate ? clone$1(lastDate) : null);

                        if (lastDate) {
                            counter.previouslyScheduledDate = clone$1(lastDate);
                        }
                    }

                    // remember last date
                    lastDate = clone$1(nextDate);

                    // calculate new duration
                    counter.value = dateDiff(now$$1, nextDate, options.format, options.cascade);
                    counter.onupdate(counter.value);
                };

                // start our counter
                counter.timer = setTimer(count, options.interval, {
                    autostart: false
                });

                // ready!
                counter.complete = true;
                counter.onload();

                // go!
                counter.timer.start();
            });

            return counter;
        };

        var support = (function() {

            var w = window;
            if (typeof w === 'undefined') {
                return false;
            }

            // test if can use CSS supports feature detection 
            var canSupport = w.CSS && w.CSS.supports;

            // test if is IE 11
            // does not support CSS.supports but does support transforms without prefix
            var isIE11 = !!w.MSInputMethodContext && !!document.documentMode;

            // test if has transform support
            // we ignore the custom Opera implementation
            var canTransform = canSupport && CSS.supports('transform', 'translateX(0)');

            // can we use mutation observer and request animation frame
            var features = ['MutationObserver', 'requestAnimationFrame'];

            // test if is supported
            return isIE11 || canSupport && canTransform && !!features.filter(function(p) {
                return p in w;
            }).length;
        });

        var transform = (function() {
            for (var _len = arguments.length, transforms = Array(_len), _key = 0; _key < _len; _key++) {
                transforms[_key] = arguments[_key];
            }

            return function(value, cb) {

                var output = [];
                var input = value;

                transforms.forEach(function(t, i) {

                    t(input, function(out) {

                        output[i] = out;

                        if (i === transforms.length - 1) {
                            cb(output.length === 1 ? output[0] : output);
                        }
                    });
                });
            };
        });

        var pad = (function() {
            var padding = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
            var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'left';
            return function(value, cb) {
                return cb(padding.length > ('' + value).length ? side === 'left' ? ('' + padding + value).slice(-padding.length) : ('' + value + padding).substring(0, padding.length) : value);
            };
        });

        var ascii = (function() {
            return function(value, cb) {
                return cb((value + '').charCodeAt(0));
            };
        });

        var add$1 = (function(amount) {
            return function(value, cb) {
                return cb(value + amount);
            };
        });

        var abs = (function() {
            return function(value, cb) {
                return cb(Math.abs(value));
            };
        });

        var value$1 = (function(staticValue) {
            return function(value, cb) {
                return cb(staticValue);
            };
        });

        var modulus = (function(amount) {
            return function(value, cb) {
                return cb(value % amount);
            };
        });

        var subtract = (function(amount) {
            return function(value, cb) {
                return cb(value - amount);
            };
        });

        var replace = (function(needle, replacement) {
            return function(string, cb) {
                return cb((string + '').replace(new RegExp(needle === '.' ? '\\' + needle : needle, 'g'), replacement));
            };
        });

        var round = (function() {
            var decimals = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
            return function(value, cb) {
                return cb(decimals ? value.toFixed(decimals) : Math.round(value));
            };
        });

        var floor = (function() {
            return function(value, cb) {
                return cb(Math.floor(value));
            };
        });

        var ceil = (function() {
            return function(value, cb) {
                return cb(Math.ceil(value));
            };
        });

        var fraction = (function() {
            var min = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
            var max = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100;
            return function(value, cb) {
                return cb((parseFloat(value) - min) / (max - min));
            };
        });

        var multiply = (function(amount) {
            return function(value, cb) {
                return cb(value * amount);
            };
        });

        var divide = (function(amount) {
            return function(value, cb) {
                return cb(value / amount);
            };
        });

        var format = (function(template) {
            return function(value, cb) {
                return cb(template.replace(/\$0/gi, value));
            };
        });

        var split = (function() {
            var character = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
            return function(string, cb) {
                return cb((string + '').split(character));
            };
        });

        var plural = (function(single, plural) {
            return function(value, cb) {
                return cb(value === 1 ? single : plural);
            };
        });

        var limit = (function() {
            var min = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
            var max = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
            return function(value, cb) {
                return cb(Math.min(Math.max(value, min), max));
            };
        });

        var reverse = (function() {
            return function(value, cb) {
                return cb(Array.isArray(value) ? value.reverse() : (value + '').split('').reverse().join(''));
            };
        });

        var arrive$1 = (function(maxVelocity, friction) {
            var resetToBegin = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
            var catchUp = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;

            var initial = null;
            var previous = null;
            var translator = null;
            return function(value, cb) {
                value = parseFloat(value);

                if (initial === null) {
                    initial = value;
                    cb(value);
                    return;
                }

                if (resetToBegin && previous !== null && initial === value) {
                    translator.cancel();
                    translator = null;
                }

                if (catchUp && previous !== null && value - translator.getPosition() > 1) {
                    translator.cancel();
                    translator = null;
                    previous = null;
                    initial = value;
                    cb(value);
                    return;
                }

                if (!translator) {
                    translator = createTranslator('arrive', maxVelocity, friction);
                    translator.update(cb, initial, value);
                } else {
                    translator.update(cb, value);
                }

                previous = value;
            };
        });

        var spring$1 = (function(stiffness, damping, mass) {

            var current = null;
            var translator = null;

            return function(value, cb) {

                value = parseFloat(value);

                if (current === null) {
                    current = value;
                    cb(value);
                    return;
                }

                if (!translator) {
                    translator = createTranslator('spring', stiffness, damping, mass);
                    translator.update(cb, current, value);
                } else {
                    translator.update(cb, value);
                }
            };
        });

        /**
         * @param order { String } - order of flipping > random | ltr | rtl (default)
         * @param min { Number } - min random delay
         * @param max { Number } - max random delay
         */
        var delay = (function() {
            var order = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'rtl';
            var min = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 50;
            var max = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 50;


            var current = null;

            return function(value, cb) {

                // if no current value, set current value and -> exit
                if (!current) {
                    current = copyArray(value);
                    cb(copyArray(current));
                    return;
                }

                current = order === 'rtl' ? current.slice(current.length - value.length, current.length) : current.slice(0, value.length);

                var indexes = range(value.length);

                if (order === 'random') {
                    shuffle(indexes);
                }

                if (order === 'rtl') {
                    indexes.reverse();
                }

                var update = function update() {
                    flip(indexes.shift(), current, value, cb);
                    if (indexes.length) {
                        setTimeout(update, random(min, max));
                    }
                };

                update();
            };
        });

        var flip = function flip(index, current, next, cb) {
            current[index] = next[index];
            cb(copyArray(current));
        };

        var number = (function() {
            var decimalsSeparator = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '.';
            var thousandsSeparator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ',';
            var decimals = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 2;
            return function(value, cb) {
                cb((value < 0 ? '-' : '') + parseFloat(Math.abs(value)).toFixed(decimals).replace(/./g, function(c, i, a) {
                    if (c === '.') {
                        return decimalsSeparator;
                    }
                    return i && (a.length - i) % 3 === 0 ? thousandsSeparator + c : c;
                }));
            };
        });

        var percentage = (function() {
            var min = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
            var max = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100;

            var f = fraction(min, max);
            return function(value, cb) {
                f(value, function(value) {
                    cb(value * 100);
                });
            };
        });

        var step$1 = (function(velocity) {

            var initial = null;
            var previous = null;
            var translator = null;

            return function(value, cb) {

                value = parseFloat(value);

                if (initial === null) {
                    initial = value;
                    cb(value);
                    return;
                }

                if (previous !== null && initial === value) {
                    translator.cancel();
                    translator = null;
                }

                if (!translator) {
                    translator = createTranslator('step', velocity);
                    translator.update(cb, initial, value);
                } else {
                    translator.update(cb, value);
                }

                previous = value;
            };
        });

        var upper = (function() {
            return function(value, cb) {
                return cb((value + '').toUpperCase());
            };
        });

        var lower = (function() {
            return function(value, cb) {
                return cb((value + '').toLowerCase());
            };
        });

        /**
         * milliseconds duration
         * @param format
         * @returns {function(*=, *)}
         */
        var duration$1 = (function() {
            for (var _len = arguments.length, format = Array(_len), _key = 0; _key < _len; _key++) {
                format[_key] = arguments[_key];
            }

            return function(value, cb) {
                return cb(timeDuration(value, format));
            };
        });

        var keys = (function() {
            for (var _len = arguments.length, keys = Array(_len), _key = 0; _key < _len; _key++) {
                keys[_key] = arguments[_key];
            }

            return function(value, cb) {
                var output = {};
                value.forEach(function(v, i) {
                    output[keys[i]] = v;
                });
                cb(output);
            };
        });

        var map = (function(transform) {
            return function(value, cb) {

                var output = [];
                var input = value;

                input.forEach(function(v, vi) {

                    transform(v, function(out) {

                        output[vi] = out;

                        if (vi === input.length - 1) {
                            cb(output.concat());
                        }
                    });
                });
            };
        });

        var rotate = (function() {
            for (var _len = arguments.length, transforms = Array(_len), _key = 0; _key < _len; _key++) {
                transforms[_key] = arguments[_key];
            }

            return function(value, cb) {

                var input = Array.isArray(value) ? value : [value];
                var output = [];
                var totalTransforms = transforms.length;

                input.forEach(function(v, i) {

                    transforms[i % totalTransforms](v, function(out) {

                        output[i] = out;
                        if (i === input.length - 1) {
                            cb(output);
                        }
                    });
                });
            };
        });

        var input = (function() {
            return function(value, cb) {
                return cb(value);
            };
        });

        var substring = (function(from, to) {
            return function(value, cb) {
                return cb((value + '').substring(from, to));
            };
        });

        var tween = (function(duration) {
            var ease = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'ease-linear';
            var delay = arguments[2];


            duration = toDuration(duration);

            var easeFn = getExtension(ExtensionType.EASING_FUNCTION, ease);
            var cancel = null;
            var previous = null;

            return function(value, cb) {

                value = parseFloat(value);

                if (cancel) {
                    cancel();
                }

                // force value if
                // - no previous value defined
                // - is same value
                // - distance between from and to is too large
                if (previous === null || value === previous) {
                    previous = value;
                    cb(value);
                    return;
                }

                var to = value;
                var from = previous;
                var dist = to - from;

                cancel = animate(function(p) {
                    cb(from + p * dist);
                }, function() {
                    cancel = null;
                }, duration, easeFn, delay);

                previous = value;
            };
        });

        var preset = (function() {
            for (var _len = arguments.length, presets = Array(_len), _key = 0; _key < _len; _key++) {
                presets[_key] = arguments[_key];
            }

            return function(value, cb, instance) {
                return cb(value.map(function(v, index) {
                    return instance.getPreset(presets[index])(v, instance.getConstants(), instance);
                }));
            };
        });

        var char = (function(filter) {
            var replacement = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';


            var regex = filter ? new RegExp('[^' + filter + ']', 'g') : null;

            return function(value, cb) {
                var char = String.fromCharCode(value);
                if (regex) {
                    char = char.replace(regex, replacement);
                }
                cb(char);
            };
        });

        var Transforms = {
            ascii: ascii,
            char: char,
            tween: tween,
            value: value$1,
            input: input,
            rotate: rotate,
            map: map,
            transform: transform,
            upper: upper,
            lower: lower,
            abs: abs,
            add: add$1,
            subtract: subtract,
            modulus: modulus,
            pad: pad,
            number: number,
            replace: replace,
            round: round,
            ceil: ceil,
            floor: floor,
            fraction: fraction,
            percentage: percentage,
            multiply: multiply,
            divide: divide,
            split: split,
            format: format,
            plural: plural,
            limit: limit,
            reverse: reverse,
            arrive: arrive$1,
            spring: spring$1,
            delay: delay,
            step: step$1,
            keys: keys,
            duration: duration$1,
            substring: substring,
            preset: preset
        };

        addExtensions(ExtensionType.TRANSFORM, Transforms);

        var crossfade = (function() {
            var speed = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
            var delayIn = arguments[1];
            var delayOut = arguments[2];

            return {
                intro: [{
                    name: 'fade',
                    parameters: [0, 1],
                    duration: 1000 * speed,
                    delay: toDuration(delayIn)
                }],
                outro: [{
                    name: 'fade',
                    parameters: [1, 0],
                    duration: 1000 * speed,
                    delay: toDuration(delayOut)
                }]
            };
        });

        var swap = (function() {
            var axis = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'y';
            var distance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
            var speed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
            var delayIn = arguments[3];
            var delayOut = arguments[4];

            return {
                intro: [{
                    name: 'move',
                    parameters: ['' + -distance * 100, '0%', axis],
                    duration: 1000 * speed,
                    delay: toDuration(delayIn)
                }],
                outro: [{
                    name: 'move',
                    parameters: ['0%', '' + distance * 100, axis],
                    duration: 1000 * speed,
                    delay: toDuration(delayOut)
                }]
            };
        });

        var revolve = (function() {
            var axis = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'y';
            var distance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
            var speed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
            var delayIn = arguments[3];
            var delayOut = arguments[4];

            return {
                intro: [{
                    name: 'rotate',
                    parameters: [-distance * 90 + 'deg', '0deg', axis],
                    duration: 1000 * speed,
                    delay: toDuration(delayIn)
                }],
                outro: [{
                    name: 'rotate',
                    parameters: ['0deg', distance * 90 + 'deg', axis],
                    duration: 1000 * speed,
                    delay: toDuration(delayOut)
                }]
            };
        });

        var zoom = (function() {
            var offset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
            var speed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
            var delayIn = arguments[2];
            var delayOut = arguments[3];

            return {
                intro: [{
                    name: 'scale',
                    parameters: [offset, 1],
                    duration: 1000 * speed,
                    delay: toDuration(delayIn)
                }],
                outro: [{
                    name: 'scale',
                    parameters: [1, offset],
                    duration: 1000 * speed,
                    delay: toDuration(delayOut)
                }]
            };
        });

        var Translation = {
            'x': 'translateX',
            'y': 'translateY',
            'z': 'translateZ'
        };

        var Rotation = {
            'x': 'rotateX',
            'y': 'rotateY',
            'z': 'rotateZ'
        };

        var Scalar = {
            'both': 'scale',
            'x': 'scaleX',
            'y': 'scaleY'
        };

        /**
         * Helper methods
         */
        var between = function between(from, to, p) {
            return from + (to - from) * p;
        };

        /**
         * Single Element transitions
         */
        var fade = function fade(element, p, direction) {
            var ease = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : easeInOutQuad;
            var from = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
            var to = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1;

            if (direction < 0) {
                var _ref = [to, from];
                from = _ref[0];
                to = _ref[1];
            }
            element.style.opacity = between(from, to, ease(p));
        };

        var move = function move(element, p, direction) {
            var ease = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : easeInOutQuad;
            var from = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : '0';
            var to = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : '100%';
            var axis = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 'y';

            if (direction < 0) {
                var _ref2 = [to, from];
                from = _ref2[0];
                to = _ref2[1];
            }
            var f = cache(from, toCSSValue);
            var t = cache(to, toCSSValue);
            setTransform(element, Translation[axis], between(f.value, t.value, ease(p)), f.units || t.units);
        };

        var rotate$1 = function rotate(element, p, direction) {
            var ease = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : easeInOutQuad;
            var from = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : '0';
            var to = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : '90deg';
            var axis = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 'x';

            if (direction < 0) {
                var _ref3 = [to, from];
                from = _ref3[0];
                to = _ref3[1];
            }
            var f = cache(from, toCSSValue);
            var t = cache(to, toCSSValue);
            setTransform(element, Rotation[axis], between(f.value, t.value, ease(p)), f.units || t.units);
        };

        var scale = function scale(element, p, direction) {
            var ease = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : easeInOutQuad;
            var from = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
            var to = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1;
            var axis = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 'both';

            if (direction < 0) {
                var _ref4 = [to, from];
                from = _ref4[0];
                to = _ref4[1];
            }
            setTransform(element, Scalar[axis], between(from, to, ease(p)));
        };

        /**
         * Composed
         */
        /**
         * Available transitions
         */
        var Transitions = {
            fade: fade,
            move: move,
            rotate: rotate$1,
            scale: scale,

            // composed transitions
            crossfade: crossfade,
            swap: swap,
            revolve: revolve,
            zoom: zoom
        };

        addExtensions(ExtensionType.TRANSITION, Transitions);

        /**
         * Helper methods for building the API
         */
        /**
         * We wan't to be sure Rollup includes these collections in the output packages so that's why they are referenced here
         */
        var API = {

            /**
             * Quick way to detect if Tick is supported
             */
            supported: support(),

            // options
            options: {
                setConstant: setConstant,
                setPreset: setPreset
            },

            /**
             * Helper Methods
             */
            helper: {

                // Starts an interval and calls callback method on each tick
                interval: setTimer,

                // Returns current time or date object based on ISO
                date: function date(iso) {
                    return iso ? dateFromISO(iso) : now$1();
                },

                // Returns duration in milliseconds or duration between two dates
                duration: duration
            },

            /**
             * Data Access
             */
            data: {

                // Request data from a url
                request: request,

                // Poll a URL for data with a set interval
                poll: function poll(url, cb) {
                    var interval = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 60000;

                    return setTimer(function() {
                        request(url, cb);
                    }, interval);
                }

            },

            /**
             * DOM Operations
             */
            DOM: {

                // Create a new ticker
                create: create,

                // Destroy an existing ticker
                destroy: destroy,

                // Parse a piece of the DOM for tickers
                parse: parse,

                // Find a specific ticker by DOM node
                find: find

            },

            count: {
                down: function down() {
                    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
                        args[_key] = arguments[_key];
                    }

                    // if is `amount` and `unit type`, 10 seconds
                    if (typeof args[0] === 'number' && typeof args[1] === 'string') {
                        var value = args[0];
                        var units = args[1].toLowerCase();
                        args.shift();
                        args[0] = duration(value, units);
                        args[1] = args[1] || {};
                        args[1].units = units;
                        return countdownAmount.apply(undefined, args);
                    }

                    // is date or iso string
                    if (typeof args[0] === 'string' || isDate(args[0])) {
                        return countdownDuration.apply(undefined, args);
                    }

                    return null;
                },
                up: countUpDuration,
                schedule: countScheduled
            },

            /**
             * Public method to extend Tick functionality
             */
            plugin: {
                add: function add(type, name, fn) {
                    if (typeof type === 'function') {
                        var extension = type;
                        return addExtension(extension.identifier.type, extension.identifier.name, extension);
                    }
                    return addExtension(type, name, fn);
                }
            }

        };

        // expose shortcut methods

        var _loop = function _loop(type) {
            if (!ExtensionType.hasOwnProperty(type)) {
                return 'continue';
            }
            API.plugin[dashesToCamels('add-' + ExtensionType[type])] = function(name, fn) {
                addExtension(ExtensionType[type], name, fn);
            };
        };

        for (var type in ExtensionType) {
            var _ret = _loop(type);

            if (_ret === 'continue') continue;
        }

        module.exports = API;

        return module.exports;
    }());

    (function() {

        // convert array to params
        function add(arr) {
            Tick.plugin.add.apply(null, arr);
        }

        // create fake push method for new extensions
        Tick.push = add;

        // loop over already pushed extensions and add to Tick
        plugins.forEach(add);

        // expose globally (overwriting extension array)
        root.Tick = Tick;

        /**
         * Auto parses document for Tick elements
         */
        function kick() {
            Tick.DOM.parse(document);
        }

        if (document.readyState !== 'loading') {
            // make sure kick is called async (same as when called on DOMContentLoaded)
            setTimeout(function() {
                kick();
            }, 0)
        } else {
            document.addEventListener('DOMContentLoaded', kick);
        }

    }());

}(window, window.Tick || []));